Skip to content
This repository was archived by the owner on Dec 22, 2022. It is now read-only.

Commit ff6b97a

Browse files
committed
Initial release
1 parent 0d0d9f7 commit ff6b97a

22 files changed

Lines changed: 1054 additions & 1 deletion

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,17 @@
11
# VideoComparison
2-
A video comparison tool which use pHash and is based on java.
2+
A video comparison tool based on java. This tool uses the pHash algorithm to hash a stack of frames (average frame per second). At the moment only the Windows operating system is supported. Depending on interests Linux and MacOS will follow.
3+
This tool uses the OpenCV-411 and FFMPEG library, which are noted in `src/lib`. These files are not included in this repository.
4+
5+
The tools search recursively in the video directory for video. Then it generates a chain of hashes and compares the hash chains with a "Shifted Longest Common Subhash" algorithm (self defined name).
6+
The result is then written to the defined file in CSV format. This list can then be sorted by the percentage of match.
7+
8+
At the moment the following parameters are offered:
9+
```
10+
{-vd,-video_directory} Required: yes Video directory
11+
{-rp,-result_path} Required: no Result path (default './result.csv')
12+
{-pc,-phash_collection} Required: no Select a pHash collection source and/or saving destination (default '')
13+
{-t,-threads} Required: no Number of threads (default '1')
14+
{-hs,-hash_size} Required: no Size of the hash (default '256')
15+
{-senp,-save_each_n_percentage} Required: no How often (percentage steps) should the hashs be saved (0=only at end) (default '10')
16+
{-duh,-drop_unneeded_hashs} Required: no Drop unneeded hashs in given collection (default 'no')
17+
```

src/LibraryLoader.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import java.io.File;
2+
import java.io.FileOutputStream;
3+
import java.io.IOException;
4+
import java.io.InputStream;
5+
import java.io.OutputStream;
6+
7+
import exceptions.NotSupportedException;
8+
/**
9+
* Class to load the native libraries
10+
*
11+
* @author Steven
12+
* @version 0.1
13+
*/
14+
public class LibraryLoader {
15+
16+
/**
17+
* Load native libraries from given path
18+
*
19+
* @param path Path to the library
20+
* @throws Exception
21+
*/
22+
public static void loadLibrary(String path) throws IOException {
23+
InputStream inputStream = VideoComparison.class.getResourceAsStream(path);
24+
File fileOut = File.createTempFile("lib", ".dll");
25+
if (fileOut != null) {
26+
OutputStream outputStream = new FileOutputStream(fileOut);
27+
byte[] buffer = new byte[1024];
28+
int length;
29+
while ((length = inputStream.read(buffer)) > 0) {
30+
outputStream.write(buffer, 0, length);
31+
}
32+
inputStream.close();
33+
outputStream.close();
34+
System.load(fileOut.toString());
35+
}
36+
}
37+
38+
/**
39+
* Load all needed native libraries
40+
*
41+
* @throws Exception
42+
*/
43+
public static void loadLibrary() throws Exception {
44+
String osName = System.getProperty("os.name");
45+
if (osName.startsWith("Windows")) {
46+
int bitness = Integer.parseInt(System.getProperty("sun.arch.data.model"));
47+
if (bitness == 64) {
48+
loadLibrary("/lib/x64/opencv_java411.dll");
49+
loadLibrary("/lib/x64/opencv_videoio_ffmpeg411_64.dll");
50+
} else {
51+
loadLibrary("/lib/x86/opencv_java411.dll");
52+
loadLibrary("/lib/x86/opencv_videoio_ffmpeg411.dll");
53+
}
54+
} else {
55+
throw new NotSupportedException("OS \"" + osName + "\" not supported at this version.");
56+
}
57+
}
58+
}

src/VideoComparison.java

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import java.io.File;
2+
import java.text.DecimalFormat;
3+
import java.text.SimpleDateFormat;
4+
import java.util.Date;
5+
6+
import argument.definition.flag.FlagArgumentDefinition;
7+
import argument.definition.integer.IntegerArgumentDefinition;
8+
import argument.definition.integer.rules.IntegerArgumentProportionRule;
9+
import argument.definition.integer.rules.IntegerArgumentRule;
10+
import argument.definition.rules.NumberProportionRuleType;
11+
import argument.definition.string.StringArgumentDefinition;
12+
import argument.definition.string.rules.StringArgumentRule;
13+
import argument.parser.ArgumentParser;
14+
import task.data.TaskDataManager;
15+
import task.hash.PHash;
16+
import task.lcs.ShiftToLongestCommonSubstring;
17+
import task.manager.ComparisonTaskManager;
18+
import task.manager.HashTaskManager;
19+
import task.manager.TaskManager;
20+
/**
21+
* This is the main class of this application.
22+
*
23+
* @author Steven Cybinski
24+
* @version 0.1
25+
*/
26+
public class VideoComparison {
27+
/**
28+
* Main function.
29+
*
30+
* @param args
31+
*/
32+
public static void main(String[] args) {
33+
try {
34+
System.out.print(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()) + ": Parse-Arguments -> ");
35+
ArgumentParser argumentParser = new ArgumentParser(true, true, true);
36+
argumentParser.addHelpArgument(new String[] {"-h", "-help"}, "This arguments are available:");
37+
StringArgumentDefinition videoDirPath = new StringArgumentDefinition(new String[]{"-vd", "-video_directory"}, "Video directory", true, "", new StringArgumentRule[] {});
38+
StringArgumentDefinition resultPath = new StringArgumentDefinition(new String[]{"-rp", "-result_path"}, "Result path", false, "./result.csv", new StringArgumentRule[] {});
39+
StringArgumentDefinition pHashCollectionPath = new StringArgumentDefinition(new String[]{"-pc", "-phash_collection"}, "Select a pHash collection source and/or saving destination", false, "", new StringArgumentRule[] {});
40+
IntegerArgumentDefinition threads = new IntegerArgumentDefinition(new String[] {"-t", "-threads"}, "Number of threads", false, 1, new IntegerArgumentRule[] {new IntegerArgumentProportionRule(NumberProportionRuleType.GREATER_EQUAL, 1)});
41+
IntegerArgumentDefinition hashSize = new IntegerArgumentDefinition(new String[] {"-hs", "-hash_size"}, "Size of the hash", false, 256, new IntegerArgumentRule[] {new IntegerArgumentProportionRule(NumberProportionRuleType.GREATER_EQUAL, 4)});
42+
IntegerArgumentDefinition saveEachNPercentage = new IntegerArgumentDefinition(new String[] {"-senp", "-save_each_n_percentage"}, "How often (percentage steps) should the hashs be saved (0=only at end)", false, 10, new IntegerArgumentRule[] {new IntegerArgumentProportionRule(NumberProportionRuleType.GREATER_EQUAL, 0)});
43+
FlagArgumentDefinition dropUnneededHashs = new FlagArgumentDefinition(new String[] {"-duh", "-drop_unneeded_hashs"}, "Drop unneeded hashs in given collection", false, false);
44+
argumentParser.add(videoDirPath);
45+
argumentParser.add(resultPath);
46+
argumentParser.add(pHashCollectionPath);
47+
argumentParser.add(threads);
48+
argumentParser.add(hashSize);
49+
argumentParser.add(saveEachNPercentage);
50+
argumentParser.add(dropUnneededHashs);
51+
argumentParser.parse(args);
52+
System.out.println("OK");
53+
54+
if(!isValidSequenceMember(4, hashSize.getValue())) {
55+
throw new Exception("The size must be in the sequence of four (e.g. 4, 16, 64, ...)");
56+
}
57+
58+
System.out.print(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()) + ": Load library -> ");
59+
LibraryLoader.loadLibrary();
60+
System.out.println("OK");
61+
62+
System.out.print(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()) + ": Search for available videos -> ");
63+
TaskDataManager taskDataManager = new TaskDataManager();
64+
taskDataManager.searchVideos(videoDirPath.getValue());
65+
if(taskDataManager.pathsSize() <= 1) {
66+
throw new Exception("Path must contain at least two video files");
67+
}
68+
System.out.println("Found " + taskDataManager.pathsSize());
69+
70+
if(pHashCollectionPath.getValue().length() > 0) {
71+
System.out.print(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()) + ": Check for available hash collection -> ");
72+
File pHashCollectionFile = new File(pHashCollectionPath.getValue());
73+
if(pHashCollectionFile.exists() && pHashCollectionFile.isFile()) {
74+
System.out.print("[Load] ");
75+
taskDataManager.loadCollection(pHashCollectionPath.getValue());
76+
} else {
77+
System.out.print("[Create new] ");
78+
pHashCollectionFile.createNewFile();
79+
}
80+
System.out.println("OK");
81+
}
82+
83+
boolean finished = false;
84+
double[] progress = new double[] {-1, -1, -1};
85+
double previousProgress = -1;
86+
int lastSave = 0;
87+
System.out.println(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()) + ": Creat " + (threads.getValue()>1?threads.getValue() + " worker-threads":" worker-thread") + " and run hash calculation:");
88+
TaskManager taskManager = new HashTaskManager(taskDataManager, threads.getValue(), new PHash(hashSize.getValue()));
89+
taskManager.run();
90+
do {
91+
finished = taskManager.isFinished();
92+
progress = taskManager.getProgress();
93+
if(progress[1] != previousProgress) {
94+
previousProgress = progress[1];
95+
String progressbar = "";
96+
for(int i=10; i<=100; i+=10) {
97+
progressbar += (i <= progress[0])?"#":" ";
98+
}
99+
System.out.print("|" + progressbar + "| " + new DecimalFormat("000.00").format(progress[0]) + "% [" + ((int) progress[1]) + "/" + ((int) progress[2]) + "]\r");
100+
}
101+
if(pHashCollectionPath.getValue().length() > 0 && saveEachNPercentage.getValue() != 0 && lastSave + saveEachNPercentage.getValue() <= progress[0]) {
102+
while(lastSave + saveEachNPercentage.getValue() <= progress[0]) {
103+
lastSave += saveEachNPercentage.getValue();
104+
}
105+
taskDataManager.saveCollection(pHashCollectionPath.getValue(), true);
106+
}
107+
Thread.sleep(10);
108+
} while(!finished);
109+
110+
if(pHashCollectionPath.getValue().length() > 0) {
111+
taskDataManager.saveCollection(pHashCollectionPath.getValue(), !dropUnneededHashs.getValue());
112+
}
113+
114+
System.out.println("");
115+
System.out.print(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()) + ": Create compare list -> ");
116+
taskDataManager.generateComparisonList();
117+
System.out.println("OK");
118+
119+
finished = false;
120+
previousProgress = -1;
121+
System.out.println(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()) + ": Creat " + (threads.getValue()>1?threads.getValue() + " worker-threads":" worker-thread") + " and run comparison:");
122+
taskManager = new ComparisonTaskManager(taskDataManager, threads.getValue(), new ShiftToLongestCommonSubstring(hashSize.getValue()));
123+
taskManager.run();
124+
do {
125+
finished = taskManager.isFinished();
126+
progress = taskManager.getProgress();
127+
if(progress[1] != previousProgress) {
128+
previousProgress = progress[1];
129+
String progressbar = "";
130+
for(int i=10; i<=100; i+=10) {
131+
progressbar += (i <= progress[0])?"#":" ";
132+
}
133+
System.out.print("|" + progressbar + "| " + new DecimalFormat("000.00").format(progress[0]) + "% [" + ((int) progress[1]) + "/" + ((int) progress[2]) + "]\r");
134+
}
135+
Thread.sleep(10);
136+
} while(!finished);
137+
138+
System.out.println("\r\n" + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()) + ": ");
139+
taskDataManager.saveResult(resultPath.getValue());
140+
System.out.println("OK");
141+
142+
System.out.println(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()) + ": Finished.");
143+
} catch (Exception e) {
144+
System.err.println(e.getMessage());
145+
e.printStackTrace();
146+
}
147+
}
148+
149+
/**
150+
* Check if a given number is in a given sequence.
151+
*
152+
* @param seq Sequence where n must be included
153+
* @param n Number to check
154+
* @return Is n a member of the given sequence
155+
*/
156+
static boolean isValidSequenceMember(int seq, int n) {
157+
int nBinary = seq;
158+
do {
159+
if (n == nBinary) {
160+
return true;
161+
}
162+
nBinary *= seq;
163+
} while (nBinary <= n);
164+
return false;
165+
}
166+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package exceptions;
2+
/**
3+
* Simple custom exception class if class without functionality is used
4+
*
5+
* @author Steven Cybinski
6+
* @version 0.1
7+
*/
8+
public class NotExtendsException extends Exception {
9+
private static final long serialVersionUID = 2177455068671395760L;
10+
/**
11+
* Constructor of the class
12+
*
13+
* @param message Message of this exception
14+
*/
15+
public NotExtendsException(String message) {
16+
super(message);
17+
}
18+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package exceptions;
2+
/**
3+
* Simple custom exception class if action is not supported in this version
4+
*
5+
* @author Steven Cybinski
6+
* @version 0.1
7+
*/
8+
public class NotSupportedException extends Exception {
9+
private static final long serialVersionUID = 1889837977001071866L;
10+
11+
/**
12+
* Constructor of the class
13+
*
14+
* @param message Message of this exception
15+
*/
16+
public NotSupportedException(String message) {
17+
super(message);
18+
}
19+
}

src/lib/ArgParser_0.1.jar

305 KB
Binary file not shown.

src/lib/opencv-411.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
opencv-411.jar

src/lib/x64/opencv_java411.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
opencv_java411.dll
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
opencv_videoio_ffmpeg411_64.dll

src/lib/x86/opencv_java411.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
opencv_java411.dll

0 commit comments

Comments
 (0)