source: other-projects/is-sheet-music-encore/trunk/image-identification-terminal/javaClassifierComparison.java@ 33439

Last change on this file since 33439 was 33439, checked in by cpb16, 5 years ago

Have created properties file and accessibility from javaClassifierComparision. Have created download middle page only variant of image-downloader system. Have created EndToEndSystem bash script that will be used as makefile, with makefile being used to execute preset useful commands to the EndToEndSystem. It needs to be fleshed out as only has one script, this script can run a classifier and place the results into a subdirectory defined by the user

File size: 24.0 KB
Line 
1
2import org.opencv.core.*;
3import org.opencv.core.Point;
4import org.opencv.highgui.HighGui;
5import org.opencv.imgcodecs.Imgcodecs;
6import org.opencv.imgproc.Imgproc;
7import static org.opencv.imgcodecs.Imgcodecs.imwrite;
8import java.awt.image.BufferedImage;
9import java.awt.image.DataBufferByte;
10import java.io.FileInputStream;
11import java.io.IOException;
12//import java.io.File;
13//import java.io.BufferedWriter;
14//import java.io.FileWriter;
15import javax.imageio.ImageIO;
16//import java.util.logging.Logger;
17//import java.util.ArrayList;
18//import java.util.Collections.*;
19import java.util.*;
20//import java.util.Properties;
21import java.lang.*;
22import java.io.*;
23
24//REFERENCES:
25//https://docs.opencv.org/3.4.3/d9/db0/tutorial_hough_lines.
26//https://stackoverflow.com/questions/43443309/count-red-pixel-in-a-given-image
27//https://www.wikihow.com/Calculate-Percentage-in-Java
28//https://riptutorial.com/opencv/example/21963/converting-an-mat-object-to-an-bufferedimage-object
29//https://stackoverflow.com/questions/15758685/how-to-write-logs-in-text-file-when-using-java-util-logging-logger
30//https://stackoverflow.com/questions/9961292/write-to-text-file-without-overwriting-in-java
31//https://alvinalexander.com/java/edu/pj/pj010005
32//https://stackoverflow.com/questions/8557716/how-to-return-multiple-values
33
34//OUTPUT OF THIS JAVA PROGRAM FOUND IN log.txt
35//Each image processed will have an output of
36//True =classifierType + 1 + Filename + Status
37//False =classifierType + 0 + Filename + Status
38public class javaClassifierComparison {
39
40//*******************************************************************************************
41//GLOBALS
42//*******************************************************************************************
43/*
44 //HOUGHLINE
45 static int CLASSIFIER_HOUGHLINESP_MIN = 10;
46 static int CLASSIFIER_HOUGHLINESP_MAX = 65;
47 static int HOUGHLINEP_THRESHOLD = 10;
48 static int STANDARD_DEVIATION_THRESHOLD = 6;
49 static int MINLINECOUNT = 40;
50 static int MAXLINEGAP = 1;
51 static double THRESHOLD_C = 4;
52 static double SLOPEGRADIENT = 0.02;
53 static double CLUSTER_DISTANCE_MAX = 40;
54 static double CLUSTER_DISTANCE_MIN = 2;
55 //MORPHOLOGY
56 static double THRESHOLD_AREA_SIZE = 1000;
57 static double THRESHOLD_AREA_COUNT = 4;
58*/
59 static int CLASSIFIER_HOUGHLINESP_MIN;
60 static int CLASSIFIER_HOUGHLINESP_MAX;
61 static int HOUGHLINEP_THRESHOLD;
62 static int STANDARD_DEVIATION_THRESHOLD;
63 static int MINLINECOUNT;
64 static int MAXLINEGAP;
65 static double THRESHOLD_C;
66 static double SLOPEGRADIENT;
67 static double CLUSTER_DISTANCE_MAX;
68 static double CLUSTER_DISTANCE_MIN;
69 //MORPHOLOGY
70 static double THRESHOLD_AREA_SIZE;
71 static double THRESHOLD_AREA_COUNT;
72
73//********************************************************************************************
74//CLASSES
75//********************************************************************************************
76
77 //Sets globals based off properties file
78 public static void init(){
79 try{
80 Properties config = new Properties();
81 FileInputStream input = new FileInputStream("configClassifierComparison.properties");
82 config.load(input);
83 CLASSIFIER_HOUGHLINESP_MIN = Integer.parseInt(config.getProperty("CLASSIFIER_HOUGHLINESP_MIN"));
84 CLASSIFIER_HOUGHLINESP_MAX = Integer.parseInt(config.getProperty("CLASSIFIER_HOUGHLINESP_MAX"));
85 HOUGHLINEP_THRESHOLD = Integer.parseInt(config.getProperty("HOUGHLINEP_THRESHOLD"));
86 STANDARD_DEVIATION_THRESHOLD = Integer.parseInt(config.getProperty("STANDARD_DEVIATION_THRESHOLD"));
87 MINLINECOUNT = Integer.parseInt(config.getProperty("MINLINECOUNT"));
88 MAXLINEGAP = Integer.parseInt(config.getProperty("MAXLINEGAP"));
89 THRESHOLD_C = Double.parseDouble(config.getProperty("THRESHOLD_C"));
90 SLOPEGRADIENT = Double.parseDouble(config.getProperty("SLOPEGRADIENT"));
91 CLUSTER_DISTANCE_MAX = Double.parseDouble(config.getProperty("CLUSTER_DISTANCE_MAX"));
92 CLUSTER_DISTANCE_MIN = Double.parseDouble(config.getProperty("CLUSTER_DISTANCE_MIN"));
93 THRESHOLD_AREA_SIZE = Double.parseDouble(config.getProperty("THRESHOLD_AREA_SIZE"));
94 THRESHOLD_AREA_COUNT = Double.parseDouble(config.getProperty("THRESHOLD_AREA_COUNT"));
95 }
96 catch(Exception e){
97 e.printStackTrace();
98 }
99 }
100
101 static public class StartAndEndPoint {
102 //PRIVATES
103 private Point _p1;
104 private Point _p2;
105
106 //CONSTRUCTOR
107 public StartAndEndPoint(Point p1, Point p2) {
108 _p1 = p1;
109 _p2 = p2;
110 }
111
112 //GETTERS
113 public Point getP1() {
114 return _p1;
115 }
116
117 public Point getP2() {
118 return _p2;
119 }
120
121 //SETTERS
122 public void setP1(Point p1) {
123 _p1 = p1;
124 }
125
126 public void setP2(Point p2) {
127 _p2 = p2;
128 }
129
130 //ToString
131 public String toString() {
132 return "Start: " + _p1 + " End: " + _p2;
133 }
134 }
135 static public class Pair{
136 //Privates
137 private Boolean _b;
138 private Integer _i;
139
140 //Constructor
141 public Pair(Boolean b, Integer i){
142 _b = b;
143 _i = i;
144 }
145 public Pair(){
146 _b = null;
147 _i = null;
148 }
149
150 //Getters
151 public Boolean getBoolean() {return _b;}
152 public Integer getInteger() {return _i;}
153
154 //Setters
155 public void setBoolean (Boolean b){_b = b;}
156 public void setInteger (Integer i){_i = i;}
157
158 //ToString
159 public String toString() {return "Boolean: " + _b + " Integer: " + _i;}
160 }
161
162 public static void main(String[] args) {
163 init();
164 try {
165
166 if (args.length != 3) {
167 System.out.println("Usage: imageClassifier <inputFilename> <classifierType> <outputFilename>");
168 } else {
169 Pair algorithmResult = new Pair();
170
171 Boolean result = null;
172 String result_cluster = "";
173 String imageFilename = args[0];
174 String classifierType = args[1];
175 String outputFilename = args[2];
176 Boolean enableLineClustering = null;
177 //Prep Writing output to disc
178 File log = new File(outputFilename);
179 FileWriter fileWriter = new FileWriter(log, true);
180 BufferedWriter bw = new BufferedWriter(fileWriter);
181 //Execute classifierType defined from arguement
182
183 //Split output by tab for processing in next java program
184 //imageFilename = 1, result = 3, classifierType = 4
185
186 switch (classifierType) {
187 case "count":
188 enableLineClustering = false;
189 algorithmResult = Algorithm_HoughLinesP_Single(imageFilename, enableLineClustering);
190 bw.write("Filename:" + '\t' + imageFilename + '\t' + "Classified as:" + '\t' + algorithmResult.getBoolean() + '\t' + "Number of lines:" + '\t' + algorithmResult.getInteger() + '\t' + classifierType + '\n');
191 break;
192 case "cluster":
193 enableLineClustering = true;
194 algorithmResult = Algorithm_HoughLinesP_Single(imageFilename, enableLineClustering);
195 bw.write("Filename:" + '\t' + imageFilename + '\t' + "Classified as:" + '\t' + algorithmResult.getBoolean() + '\t' + "Number of lines:" + '\t' + algorithmResult.getInteger() + '\t' + classifierType + '\n');
196 break;
197 case "combo":
198 algorithmResult = Algorithm_HoughLinesP_Combo(imageFilename);
199 bw.write("Filename:" + '\t' + imageFilename + '\t' + "Classified as:" + '\t' + algorithmResult.getBoolean() + '\t' + "Number of lines:" + '\t' + algorithmResult.getInteger() + '\t' + classifierType + '\n');
200 break;
201 case "morphology":
202 algorithmResult = Algorithm_Morphology(imageFilename);
203 bw.write("Filename:" + '\t' + imageFilename + '\t' + "Classified as:" + '\t' + algorithmResult.getBoolean() + '\t' + "Number of areas:" + '\t' + algorithmResult.getInteger() + '\t' + classifierType + '\n');
204 break;
205 default:
206 System.out.println("unknown algorithm");
207 break;
208 }
209
210 bw.close();
211 }
212 } catch (Exception e) {
213 System.err.println(e);
214 }
215 }
216
217 //******************
218 //ALGORITHM FUNCTIONS
219 //******************
220 private static Pair Algorithm_HoughLinesP_Single(String filename, Boolean enableLineClusterDetection){
221 System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
222 Boolean isSheetMusic = null;
223 Pair returnVariables = new Pair();
224 try{
225 //Variables
226 int horizontalLineCount =0;
227 Mat edgesDetected = new Mat();
228 Mat edgesDetectedRGB = new Mat();
229 Mat edgesExtra = new Mat();
230 Mat edgesDetectedRGBProb;
231 ArrayList<StartAndEndPoint> pointArrayList = new ArrayList<StartAndEndPoint>();
232
233 //****************EXPLANATION**************************************************
234 //
235 //Load an image in greyscale
236 //Additional matrix to hold results of line detection
237 //Inversed Binarization of image
238 //Detect lines in image
239 //Go thru every line detected and check its gradient is less than SLOPEGRADIENT
240 //
241 //****************EXPLANATION**************************************************
242
243 Mat original = Imgcodecs.imread(filename, Imgcodecs.IMREAD_GRAYSCALE);
244 Mat linesP = new Mat(); //will hold the results of the detection
245 Imgproc.adaptiveThreshold(original, edgesDetected,255, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C,Imgproc.THRESH_BINARY_INV,15, THRESHOLD_C);
246 double minLineLength = edgesDetected.size().width/8;
247 Imgproc.HoughLinesP(edgesDetected, linesP, 1, Math.PI / 720, HOUGHLINEP_THRESHOLD, minLineLength, MAXLINEGAP);
248 for (int x = 0; x < linesP.rows(); x++) {
249 double[] l = linesP.get(x, 0);
250 Point p1 = new Point(l[0], l[1]);
251 Point p2 = new Point(l[2], l[3]);
252 double m = Math.abs(p2.y - p1.y)/(p2.x - p1.x);
253 if(m<SLOPEGRADIENT) {
254 horizontalLineCount++;
255 pointArrayList.add(new StartAndEndPoint(p1, p2));
256 }
257 }
258
259 //Calculate if its sheet music or not
260 if(enableLineClusterDetection ==true){returnVariables = Classifier_ClusterDetection(pointArrayList);}
261 else {
262 isSheetMusic = Classifier_LineCounter(horizontalLineCount);
263 returnVariables.setBoolean(isSheetMusic);
264 returnVariables.setInteger(horizontalLineCount);
265 }
266 }
267 catch(Exception e){
268 System.err.println(e);
269 }
270 return returnVariables;
271 }
272 private static Pair Algorithm_HoughLinesP_Combo(String filename){
273 System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
274 Boolean isSheetMusic = null;
275 Pair returnVariables = new Pair();
276 try{
277 //Variables
278 int horizontalLineCount =0;
279 Mat edgesDetected = new Mat();
280 Mat edgesDetectedRGB = new Mat();
281 Mat edgesExtra = new Mat();
282 Mat edgesDetectedRGBProb;
283 ArrayList<StartAndEndPoint> pointArrayList = new ArrayList<StartAndEndPoint>();
284
285 //****************EXPLANATION**************************************************
286 //
287 //Load an image in greyscale
288 //Additional matrix to hold results of line detection
289 //Inversed Binarization of image
290 //Detect lines in image
291 //Go thru every line detected and check its gradient is less than SLOPEGRADIENT
292 //
293 //****************EXPLANATION**************************************************
294
295 Mat original = Imgcodecs.imread(filename, Imgcodecs.IMREAD_GRAYSCALE);
296 Mat linesP = new Mat(); //will hold the results of the detection
297 Imgproc.adaptiveThreshold(original, edgesDetected,255, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C,Imgproc.THRESH_BINARY_INV,15, THRESHOLD_C);
298 double minLineLength = edgesDetected.size().width/8;
299 Imgproc.HoughLinesP(edgesDetected, linesP, 1, Math.PI / 720, HOUGHLINEP_THRESHOLD, minLineLength, MAXLINEGAP);
300 for (int x = 0; x < linesP.rows(); x++) {
301 double[] l = linesP.get(x, 0);
302 Point p1 = new Point(l[0], l[1]);
303 Point p2 = new Point(l[2], l[3]);
304 double m = Math.abs(p2.y - p1.y)/(p2.x - p1.x);
305 if(m<SLOPEGRADIENT) {
306 horizontalLineCount++;
307 pointArrayList.add(new StartAndEndPoint(p1, p2));
308 }
309 }
310
311 //Calculate if its sheet music or not
312 isSheetMusic = Classifier_LineCounter(horizontalLineCount);
313 if(isSheetMusic == true){
314 returnVariables.setBoolean(isSheetMusic);
315 returnVariables.setInteger(horizontalLineCount);
316 }
317 else if (isSheetMusic == false){
318 returnVariables = Classifier_ClusterDetection(pointArrayList);
319 }
320 }
321 catch(Exception e){
322 System.err.println(e);
323 }
324 return returnVariables;
325 }
326 private static Pair Algorithm_Morphology(String filename){
327 System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
328 Boolean isSheetMusic = null;
329 Pair returnVariables = new Pair();
330 try{
331 //Variables
332 int areaCounter = 0;
333 Mat edgesDetectedRGB = new Mat();
334 Mat original = Imgcodecs.imread(filename, Imgcodecs.IMREAD_GRAYSCALE);
335
336 ArrayList<MatOfPoint> contours = new ArrayList<MatOfPoint>();
337 Mat hierarchy = new Mat();
338
339 //Thresholds
340 Imgproc.adaptiveThreshold(original, original,255, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C,Imgproc.THRESH_BINARY_INV, 15, THRESHOLD_C);
341 Mat processed = original.clone();
342 //Morphological Processing
343 Mat kernelErode = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(10,1));
344 Imgproc.erode(processed,processed,kernelErode);
345
346 Mat kernelDilate = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(20,3));
347 Imgproc.dilate(processed,processed,kernelDilate);
348
349 Mat kernelOpening = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(4,4));
350 Imgproc.morphologyEx(processed, processed, Imgproc.MORPH_CLOSE, kernelOpening);
351
352 Mat kernelErode02 = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(8,8));
353 Imgproc.erode(processed,processed,kernelErode02);
354
355 //Detect contours
356 Imgproc.findContours(processed, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);
357
358 //Record areas
359 for (int i = 0; i < contours.size(); i++) {
360 double area = Imgproc.contourArea(contours.get(i));
361 //Check if area detected meets threshold
362 if(area > THRESHOLD_AREA_SIZE) {
363 areaCounter++;
364 //System.out.println("AREA: " + area);
365 }
366 }
367 //Calculates if sheet music or not
368 if(areaCounter >= THRESHOLD_AREA_COUNT){
369 returnVariables.setBoolean(true);
370 returnVariables.setInteger(areaCounter);
371 }
372 }
373 catch(Exception e){
374 System.err.println(e);
375 }
376 return returnVariables;
377 }
378
379 //******************
380 //CLASSIFIER FUNCTIONS
381 //******************
382
383 private static boolean Classifier_LineCounter(int lineCount){
384 if(lineCount>MINLINECOUNT){return true;}
385 else{return false;}
386 }
387 private static Pair Classifier_ClusterDetection(ArrayList<StartAndEndPoint> linePointsArray){
388
389 Pair returnPair = new Pair();
390 ArrayList<StartAndEndPoint> closeLinePts = new ArrayList<StartAndEndPoint>();
391 ArrayList<StartAndEndPoint[]> clusterPtArray = new ArrayList<StartAndEndPoint[]>();
392 int clusterCount = 0;
393 try {
394
395 if(linePointsArray.size()> 1) {
396 for (int i = 0; i < linePointsArray.size(); i++){
397 for (int j = 0; j < linePointsArray.size(); j++) {
398 if(Math.abs(linePointsArray.get(j).getP1().y - linePointsArray.get(i).getP1().y) < 5){
399 if(linePointsArray.get(j).getP1().y != linePointsArray.get(i).getP1().y){
400 closeLinePts.add(linePointsArray.get(i));
401 }
402 }
403 }
404 }
405
406 Collections.sort(closeLinePts, new Comparator<StartAndEndPoint>() {
407 @Override
408 public int compare(StartAndEndPoint p1, StartAndEndPoint p2) {
409 return (int)(p1.getP1().y - p2.getP1().y);
410 }
411 });
412
413 closeLinePts = removeDuplicates(closeLinePts);
414
415 if(closeLinePts.size() >= 4) {
416 for(int i= 0; i < closeLinePts.size(); i++){
417 if(i + 4 >= closeLinePts.size()){
418 break;
419 }
420 else{
421 StartAndEndPoint[] tempPtArray = new StartAndEndPoint[4];
422 tempPtArray[0] = closeLinePts.get(i);
423 tempPtArray[1] = closeLinePts.get(i + 1);
424 tempPtArray[2] = closeLinePts.get(i + 2);
425 tempPtArray[3] = closeLinePts.get(i + 3);
426 if(ClusterCheck(tempPtArray)){
427 clusterPtArray.add(tempPtArray);
428 if((i + 4 < closeLinePts.size())){
429 i = i+4;
430 }
431 else{
432 break;
433 }
434 }
435 }
436 }
437 }
438
439 /* for(StartAndEndPoint pt : linePointsArray){
440 for(int i =0; i < clusterPtArray.size(); i++){
441 for(StartAndEndPoint item : clusterPtArray.get(i)) {
442 if (item.getP1().y == pt.getP1().y){
443 Imgproc.line(clustersFoundRGB, pt.getP1(), pt.getP2(), new Scalar(0, 255, 0), 1, Imgproc.LINE_4, 0);
444 }
445 }
446 }
447 }*/
448
449 clusterCount = clusterPtArray.size();
450 //SETUP RETURN ARRAY
451 if(clusterCount >= 1){
452 returnPair.setBoolean(true);
453 returnPair.setInteger(clusterCount);
454 //returnArray.add(clustersFoundRGB);
455 }
456 else{
457 returnPair.setBoolean(false);
458 returnPair.setInteger(clusterCount);
459 //returnArray.add(clustersFoundRGB);
460 }
461 }
462 }
463 catch (Exception e) {
464 System.err.println(e.getMessage());
465 }
466 return returnPair;
467 }
468
469 //******************
470 //INTERNAL FUNCTIONS
471 //******************
472
473 public static <T> ArrayList<T> removeDuplicates(ArrayList<T> list) {
474 //DIRECTLY COPIED//DIRECTLY COPIED//DIRECTLY COPIED//DIRECTLY COPIED//DIRECTLY COPIED//DIRECTLY COPIED
475 // Function to remove duplicates from an ArrayList
476 // Create a new ArrayList
477 ArrayList<T> newList = new ArrayList<T>();
478 // Traverse through the first list
479 for (T element : list) {
480 // If this element is not present in newList
481 // then add it
482 if (!newList.contains(element)) {
483 newList.add(element);
484 }
485 }
486 // return the new list
487 return newList;
488 //DIRECTLY COPIED//DIRECTLY COPIED//DIRECTLY COPIED//DIRECTLY COPIED//DIRECTLY COPIED//DIRECTLY COPIED
489 }
490 public static double VarianceCalc(StartAndEndPoint parseArray[]){
491 double sum =0;
492 double temp =0;
493 double mean, variance;
494 int size = parseArray.length;
495 //Calculate sum of array
496 for(int i =0; i < parseArray.length; i++){
497 sum += parseArray[i].getP1().y;
498 }
499 //Calculate mean of array
500 mean = sum/parseArray.length;
501 //Calculate variants
502 for(int i =0; i < size; i++){
503 temp += Math.pow((parseArray[i].getP1().y-mean),2);
504 }
505 variance = Math.abs(temp/(size -1));
506 //System.out.println("VARIANCE: " + variance);
507 return variance;
508 }
509 public static Boolean lineComparison(double baseLineS, double compareLineS, double compareLineE ){
510 //System.out.print("Comparing baseLineS: " + baseLineS + " with compareLineE: " + compareLineE + " and compareLineS: " + compareLineS);
511 if(baseLineS < compareLineE && baseLineS > compareLineS){
512 return true;
513 }
514 return false;
515 }
516 public static Boolean ClusterCheck(StartAndEndPoint parseArray[]){
517 try {
518 //System.out.println("LENGTH: " + parseArray.length);
519 //MAKE THREE COMPARISONS
520 //After clusters have been found.
521 //Check if their x positions intersect
522 //Logic being
523 //(L1.S < L2.E && L1.S > L2.S)
524 //or
525 //(L2.S < L1.E && L2.S > L1.S)
526 //Variance is using Start of line point.
527 //USING VARIANTS
528 double variance = VarianceCalc(parseArray);
529 Boolean consistent = false;
530 if (variance <= CLUSTER_DISTANCE_MAX && variance > CLUSTER_DISTANCE_MIN) {
531
532 for (int i = 0; i < parseArray.length - 1; i++) {
533 //System.out.println(i);
534 double l1_S = parseArray[i].getP1().x;
535 double l1_E = parseArray[i].getP2().x;
536 double l2_S = parseArray[i + 1].getP1().x;
537 double l2_E = parseArray[i + 1].getP2().x;
538
539 //Check which starts after
540 if (l1_S >= l2_S) {
541 //baseLineStart is l1_S (call with lineComparison)
542 consistent = lineComparison(l1_S, l2_S, l2_E);
543 } else if (l2_S > l1_S) {
544 //baseLineStart is l2_S (call with lineComparison)
545 consistent = lineComparison(l2_S, l1_S, l1_E);
546 } else {
547 System.err.println("An error, comparing l1_S and l2_S, has occurred");
548 }
549
550 //Check if false was returned;
551 if (consistent == false) {
552 /*System.out.print(" X positions of two lines did not overlap each other:" + '\t');
553 System.out.print("l1_S: " + l1_S + '\t');
554 System.out.print("l1_E: " + l1_E + '\t');
555 System.out.print("l2_S: " + l2_S + '\t');
556 System.out.print("l2_E: " + l2_E);
557 System.out.println(" ");*/
558 return false;
559 }
560 }
561 //Have been through for loop, maintaining consistent being true.
562 //Have also meet the variance MIN and MAX requirement. Therefore it is a cluster
563 return true;
564 }
565 //System.out.println("Did not meet Cluster Distance Min and Max requirements, Variance = " + variance);
566 return false;
567 }
568 catch (Exception e){
569 System.err.println(" "+e.getMessage());
570 return false;
571 }
572 }
573}
Note: See TracBrowser for help on using the repository browser.