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

Commit

Permalink
Initial release
Browse files Browse the repository at this point in the history
  • Loading branch information
StevenCyb committed Dec 11, 2019
1 parent 0d0d9f7 commit ff6b97a
Show file tree
Hide file tree
Showing 22 changed files with 1,054 additions and 1 deletion.
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,17 @@
# VideoComparison
A video comparison tool which use pHash and is based on java.
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.
This tool uses the OpenCV-411 and FFMPEG library, which are noted in `src/lib`. These files are not included in this repository.

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).
The result is then written to the defined file in CSV format. This list can then be sorted by the percentage of match.

At the moment the following parameters are offered:
```
{-vd,-video_directory} Required: yes Video directory
{-rp,-result_path} Required: no Result path (default './result.csv')
{-pc,-phash_collection} Required: no Select a pHash collection source and/or saving destination (default '')
{-t,-threads} Required: no Number of threads (default '1')
{-hs,-hash_size} Required: no Size of the hash (default '256')
{-senp,-save_each_n_percentage} Required: no How often (percentage steps) should the hashs be saved (0=only at end) (default '10')
{-duh,-drop_unneeded_hashs} Required: no Drop unneeded hashs in given collection (default 'no')
```
58 changes: 58 additions & 0 deletions src/LibraryLoader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import exceptions.NotSupportedException;
/**
* Class to load the native libraries
*
* @author Steven
* @version 0.1
*/
public class LibraryLoader {

/**
* Load native libraries from given path
*
* @param path Path to the library
* @throws Exception
*/
public static void loadLibrary(String path) throws IOException {
InputStream inputStream = VideoComparison.class.getResourceAsStream(path);
File fileOut = File.createTempFile("lib", ".dll");
if (fileOut != null) {
OutputStream outputStream = new FileOutputStream(fileOut);
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
inputStream.close();
outputStream.close();
System.load(fileOut.toString());
}
}

/**
* Load all needed native libraries
*
* @throws Exception
*/
public static void loadLibrary() throws Exception {
String osName = System.getProperty("os.name");
if (osName.startsWith("Windows")) {
int bitness = Integer.parseInt(System.getProperty("sun.arch.data.model"));
if (bitness == 64) {
loadLibrary("/lib/x64/opencv_java411.dll");
loadLibrary("/lib/x64/opencv_videoio_ffmpeg411_64.dll");
} else {
loadLibrary("/lib/x86/opencv_java411.dll");
loadLibrary("/lib/x86/opencv_videoio_ffmpeg411.dll");
}
} else {
throw new NotSupportedException("OS \"" + osName + "\" not supported at this version.");
}
}
}
166 changes: 166 additions & 0 deletions src/VideoComparison.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import java.io.File;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import argument.definition.flag.FlagArgumentDefinition;
import argument.definition.integer.IntegerArgumentDefinition;
import argument.definition.integer.rules.IntegerArgumentProportionRule;
import argument.definition.integer.rules.IntegerArgumentRule;
import argument.definition.rules.NumberProportionRuleType;
import argument.definition.string.StringArgumentDefinition;
import argument.definition.string.rules.StringArgumentRule;
import argument.parser.ArgumentParser;
import task.data.TaskDataManager;
import task.hash.PHash;
import task.lcs.ShiftToLongestCommonSubstring;
import task.manager.ComparisonTaskManager;
import task.manager.HashTaskManager;
import task.manager.TaskManager;
/**
* This is the main class of this application.
*
* @author Steven Cybinski
* @version 0.1
*/
public class VideoComparison {
/**
* Main function.
*
* @param args
*/
public static void main(String[] args) {
try {
System.out.print(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()) + ": Parse-Arguments -> ");
ArgumentParser argumentParser = new ArgumentParser(true, true, true);
argumentParser.addHelpArgument(new String[] {"-h", "-help"}, "This arguments are available:");
StringArgumentDefinition videoDirPath = new StringArgumentDefinition(new String[]{"-vd", "-video_directory"}, "Video directory", true, "", new StringArgumentRule[] {});
StringArgumentDefinition resultPath = new StringArgumentDefinition(new String[]{"-rp", "-result_path"}, "Result path", false, "./result.csv", new StringArgumentRule[] {});
StringArgumentDefinition pHashCollectionPath = new StringArgumentDefinition(new String[]{"-pc", "-phash_collection"}, "Select a pHash collection source and/or saving destination", false, "", new StringArgumentRule[] {});
IntegerArgumentDefinition threads = new IntegerArgumentDefinition(new String[] {"-t", "-threads"}, "Number of threads", false, 1, new IntegerArgumentRule[] {new IntegerArgumentProportionRule(NumberProportionRuleType.GREATER_EQUAL, 1)});
IntegerArgumentDefinition hashSize = new IntegerArgumentDefinition(new String[] {"-hs", "-hash_size"}, "Size of the hash", false, 256, new IntegerArgumentRule[] {new IntegerArgumentProportionRule(NumberProportionRuleType.GREATER_EQUAL, 4)});
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)});
FlagArgumentDefinition dropUnneededHashs = new FlagArgumentDefinition(new String[] {"-duh", "-drop_unneeded_hashs"}, "Drop unneeded hashs in given collection", false, false);
argumentParser.add(videoDirPath);
argumentParser.add(resultPath);
argumentParser.add(pHashCollectionPath);
argumentParser.add(threads);
argumentParser.add(hashSize);
argumentParser.add(saveEachNPercentage);
argumentParser.add(dropUnneededHashs);
argumentParser.parse(args);
System.out.println("OK");

if(!isValidSequenceMember(4, hashSize.getValue())) {
throw new Exception("The size must be in the sequence of four (e.g. 4, 16, 64, ...)");
}

System.out.print(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()) + ": Load library -> ");
LibraryLoader.loadLibrary();
System.out.println("OK");

System.out.print(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()) + ": Search for available videos -> ");
TaskDataManager taskDataManager = new TaskDataManager();
taskDataManager.searchVideos(videoDirPath.getValue());
if(taskDataManager.pathsSize() <= 1) {
throw new Exception("Path must contain at least two video files");
}
System.out.println("Found " + taskDataManager.pathsSize());

if(pHashCollectionPath.getValue().length() > 0) {
System.out.print(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()) + ": Check for available hash collection -> ");
File pHashCollectionFile = new File(pHashCollectionPath.getValue());
if(pHashCollectionFile.exists() && pHashCollectionFile.isFile()) {
System.out.print("[Load] ");
taskDataManager.loadCollection(pHashCollectionPath.getValue());
} else {
System.out.print("[Create new] ");
pHashCollectionFile.createNewFile();
}
System.out.println("OK");
}

boolean finished = false;
double[] progress = new double[] {-1, -1, -1};
double previousProgress = -1;
int lastSave = 0;
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:");
TaskManager taskManager = new HashTaskManager(taskDataManager, threads.getValue(), new PHash(hashSize.getValue()));
taskManager.run();
do {
finished = taskManager.isFinished();
progress = taskManager.getProgress();
if(progress[1] != previousProgress) {
previousProgress = progress[1];
String progressbar = "";
for(int i=10; i<=100; i+=10) {
progressbar += (i <= progress[0])?"#":" ";
}
System.out.print("|" + progressbar + "| " + new DecimalFormat("000.00").format(progress[0]) + "% [" + ((int) progress[1]) + "/" + ((int) progress[2]) + "]\r");
}
if(pHashCollectionPath.getValue().length() > 0 && saveEachNPercentage.getValue() != 0 && lastSave + saveEachNPercentage.getValue() <= progress[0]) {
while(lastSave + saveEachNPercentage.getValue() <= progress[0]) {
lastSave += saveEachNPercentage.getValue();
}
taskDataManager.saveCollection(pHashCollectionPath.getValue(), true);
}
Thread.sleep(10);
} while(!finished);

if(pHashCollectionPath.getValue().length() > 0) {
taskDataManager.saveCollection(pHashCollectionPath.getValue(), !dropUnneededHashs.getValue());
}

System.out.println("");
System.out.print(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()) + ": Create compare list -> ");
taskDataManager.generateComparisonList();
System.out.println("OK");

finished = false;
previousProgress = -1;
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:");
taskManager = new ComparisonTaskManager(taskDataManager, threads.getValue(), new ShiftToLongestCommonSubstring(hashSize.getValue()));
taskManager.run();
do {
finished = taskManager.isFinished();
progress = taskManager.getProgress();
if(progress[1] != previousProgress) {
previousProgress = progress[1];
String progressbar = "";
for(int i=10; i<=100; i+=10) {
progressbar += (i <= progress[0])?"#":" ";
}
System.out.print("|" + progressbar + "| " + new DecimalFormat("000.00").format(progress[0]) + "% [" + ((int) progress[1]) + "/" + ((int) progress[2]) + "]\r");
}
Thread.sleep(10);
} while(!finished);

System.out.println("\r\n" + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()) + ": ");
taskDataManager.saveResult(resultPath.getValue());
System.out.println("OK");

System.out.println(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()) + ": Finished.");
} catch (Exception e) {
System.err.println(e.getMessage());
e.printStackTrace();
}
}

/**
* Check if a given number is in a given sequence.
*
* @param seq Sequence where n must be included
* @param n Number to check
* @return Is n a member of the given sequence
*/
static boolean isValidSequenceMember(int seq, int n) {
int nBinary = seq;
do {
if (n == nBinary) {
return true;
}
nBinary *= seq;
} while (nBinary <= n);
return false;
}
}
18 changes: 18 additions & 0 deletions src/exceptions/NotExtendsException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package exceptions;
/**
* Simple custom exception class if class without functionality is used
*
* @author Steven Cybinski
* @version 0.1
*/
public class NotExtendsException extends Exception {
private static final long serialVersionUID = 2177455068671395760L;
/**
* Constructor of the class
*
* @param message Message of this exception
*/
public NotExtendsException(String message) {
super(message);
}
}
19 changes: 19 additions & 0 deletions src/exceptions/NotSupportedException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package exceptions;
/**
* Simple custom exception class if action is not supported in this version
*
* @author Steven Cybinski
* @version 0.1
*/
public class NotSupportedException extends Exception {
private static final long serialVersionUID = 1889837977001071866L;

/**
* Constructor of the class
*
* @param message Message of this exception
*/
public NotSupportedException(String message) {
super(message);
}
}
Binary file added src/lib/ArgParser_0.1.jar
Binary file not shown.
1 change: 1 addition & 0 deletions src/lib/opencv-411.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
opencv-411.jar
1 change: 1 addition & 0 deletions src/lib/x64/opencv_java411.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
opencv_java411.dll
1 change: 1 addition & 0 deletions src/lib/x64/opencv_videoio_ffmpeg411_64.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
opencv_videoio_ffmpeg411_64.dll
1 change: 1 addition & 0 deletions src/lib/x86/opencv_java411.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
opencv_java411.dll
1 change: 1 addition & 0 deletions src/lib/x86/opencv_videoio_ffmpeg411.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
opencv_videoio_ffmpeg411.dll
Loading

0 comments on commit ff6b97a

Please sign in to comment.