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

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

updated, after unable to commit due to setup.bash being out of date. Added mainMorph for morphological development

File size: 23.7 KB
Line 
1import org.opencv.core.*;
2import org.opencv.core.Point;
3import org.opencv.highgui.HighGui;
4import org.opencv.imgcodecs.Imgcodecs;
5import org.opencv.imgproc.Imgproc;
6import static org.opencv.imgcodecs.Imgcodecs.imwrite;
7import java.awt.image.BufferedImage;
8import java.awt.image.DataBufferByte;
9//import java.io.File;
10//import java.io.BufferedWriter;
11//import java.io.FileWriter;
12import javax.imageio.ImageIO;
13//import java.util.logging.Logger;
14//import java.util.ArrayList;
15//import java.util.Collections.*;
16import java.util.*;
17import java.lang.*;
18import java.io.*;
19
20//REFERENCES:
21//https://docs.opencv.org/3.4.3/d9/db0/tutorial_hough_lines.
22//https://stackoverflow.com/questions/43443309/count-red-pixel-in-a-given-image
23//https://www.wikihow.com/Calculate-Percentage-in-Java
24//https://riptutorial.com/opencv/example/21963/converting-an-mat-object-to-an-bufferedimage-object
25//https://stackoverflow.com/questions/15758685/how-to-write-logs-in-text-file-when-using-java-util-logging-logger
26//https://stackoverflow.com/questions/9961292/write-to-text-file-without-overwriting-in-java
27
28
29//OUTPUT OF THIS JAVA PROGRAM FOUND IN log.txt
30//Each image processed will have an output of
31//True =classifierType + 1 + Filename + Status
32//False =classifierType + 0 + Filename + Status
33public class javaImageClassifier{
34 //GLOBALS Constants
35 static int CLASSIFIER_HOUGHLINESP_MIN = 10;
36 static int CLASSIFIER_HOUGHLINESP_MAX = 65;
37 static int HOUGHLINEP_THRESHOLD = 10;
38 static int STANDARD_DEVIATION_THRESHOLD = 6;
39 static int MINLINECOUNT = 40;
40 static int MAXLINEGAP = 4;
41 static double SLOPEGRADIENT = 0.02;
42 static double CLUSTER_DISTANCE_MAX = 40;
43 static double CLUSTER_DISTANCE_MIN = 2;
44
45 static class StartAndEndPoint {
46 //PRIVATES
47 private Point _p1;
48 private Point _p2;
49 //CONSTRUCTOR
50 public StartAndEndPoint(Point p1, Point p2){
51 _p1 = p1;
52 _p2 = p2;}
53 //GETTERS
54 public Point getP1(){return _p1;}
55 public Point getP2(){return _p2;}
56 //SETTERS
57 public void setP1(Point p1){_p1 = p1;}
58 public void setP2(Point p2){_p2 = p2;}
59 //ToString
60 public String toString(){
61 return "Start: " + _p1 + " End: " + _p2;
62 }
63 }
64
65 public static void main(String[] args) {
66 try {
67 if (args.length != 3) {
68 System.out.println("Usage: imageClassifier <inputFilename> <classifierType> <outputFilename>");
69 }
70 else {
71 ArrayList result_refined = null;
72 Boolean result = null;
73 String result_cluster = "";
74 String imageFilename = args[0];
75 String classifierType = args[1];
76 String outputFilename = args[2];
77 //Prep Writing output to disc
78 File log = new File(outputFilename);
79 FileWriter fileWriter = new FileWriter(log, true);
80 BufferedWriter bw = new BufferedWriter(fileWriter);
81 //Execute classifierType defined from arguement
82
83 //Split output by tab for processing in next java program
84 //imageFilename = 1, result = 3, classifierType = 4
85 switch(classifierType){
86 case "houghlinesP":
87 result = setup_HoughLinesP(imageFilename); //true or false
88 bw.write("Filename:" + '\t' + imageFilename + '\t' + "Classified as:" + '\t' + result + '\t' + classifierType + '\n');
89 break;
90 case "houghlinesP-refined":
91 result_refined = setup_HoughLinesP_refined(imageFilename);
92 bw.write("Filename:" + '\t' + imageFilename + '\t' + "Classified as:" + '\t' + result_refined.get(0) + '\t' + "Number of lines:" + '\t' + result_refined.get(1) + '\t' + classifierType + '\n');
93 break;
94 case "cluster-detection":
95 //result_cluster = setup_Cluster(imageFilename);
96 //bw.write(result_cluster);
97 break;
98 default:
99 System.out.println("unknown algorithm");
100 break;
101 }
102
103 bw.close();
104 }
105 }
106 catch(Exception e){
107 System.err.println(e);
108 }
109 }
110 //Returns
111 //True = 1 + Filename + Status
112 //False= 0 + Filename + Status
113
114 //******************
115 //CLASSIFIER FUNCTIONS
116 //******************
117 private static boolean setup_HoughLinesP(String filename){
118 System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
119 Boolean isSheetMusic = null;
120 try{
121 //Variables
122 Mat edgesDetected = new Mat();
123 Mat edgesDetectedRGB = new Mat();
124 Mat edgesExtra = new Mat();
125 Mat edgesDetectedRGBProb;
126 // Load an image
127 Mat original = Imgcodecs.imread(filename, Imgcodecs.IMREAD_GRAYSCALE);
128 // Edge detection
129 Imgproc.Canny(original, edgesDetected, 50, 200, 3, false);
130 //Copy edges to the images that will display the results in BGR
131 Imgproc.cvtColor(edgesDetected, edgesDetectedRGB, Imgproc.COLOR_GRAY2BGR);
132 // Probabilistic Line Transform
133 Mat linesP = new Mat(); // will hold the results of the detection
134 Imgproc.HoughLinesP(edgesDetected, linesP, 1, Math.PI / 180, 50, 50, 10); // runs the actual detection
135 // Draw the lines
136 for (int x = 0; x < linesP.rows(); x++) {
137 double[] l = linesP.get(x, 0);
138 Imgproc.line(edgesDetectedRGB, new Point(l[0], l[1]), new Point(l[2], l[3]), new Scalar(0, 0, 255), 3, Imgproc.LINE_AA, 0);
139 }
140
141 //Convert MAT into a BufferedImage
142 BufferedImage toBeClassifiedImg = toBufferedImage(edgesDetectedRGB);
143 //Calculate if its sheet music or not
144 isSheetMusic = classifier_HoughLinesP(toBeClassifiedImg);
145 }
146 catch(Exception e){
147 System.err.println(e);
148 }
149 return isSheetMusic;
150 }
151 private static boolean classifier_HoughLinesP(BufferedImage img){
152 System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
153 try {
154 //Read file
155 int x = img.getWidth();
156 int y = img.getHeight();
157 int pixelCount = 0;
158 int redCount = 0;
159 float percentage = 0;
160 //Go Thru every pixel
161 for(int i=0; i < y; i++){
162 for(int j=0;j < x; j++){
163 //Get value for current pixels RGB value
164 int currPixelRGB = img.getRGB(j, i);
165 //Check if pixel is red (hex value of red)
166 if(currPixelRGB == 0xFFFF0000){
167 redCount++;
168 }
169 pixelCount++;
170 }
171 }
172 //Calculate percentage of Red in image
173 percentage = ((float)redCount/(float)pixelCount)*(float)100;
174 //If more than %10 and less than %50 then its sheet music!
175 if(percentage > CLASSIFIER_HOUGHLINESP_MIN && percentage < CLASSIFIER_HOUGHLINESP_MAX){
176 return true;}
177 }
178 catch (Exception e) {
179 System.err.println(e);
180 }
181 return false;
182 }
183
184 private static ArrayList setup_HoughLinesP_refined(String filename){
185 System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
186 Boolean isSheetMusic = null;
187 ArrayList returnArray = new ArrayList();
188 try{
189 //Variables
190 int horizontalLineCount =0;
191 Mat edgesDetected = new Mat();
192 Mat edgesDetectedRGB = new Mat();
193 Mat edgesExtra = new Mat();
194 Mat edgesDetectedRGBProb;
195 // Load an image
196 Mat original = Imgcodecs.imread(filename, Imgcodecs.IMREAD_GRAYSCALE);
197 // Edge detection
198 Imgproc.adaptiveThreshold(original, edgesDetected,255, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C,Imgproc.THRESH_BINARY_INV,15, 2);
199 //Copy edges to the images that will display the results in BGR
200 Imgproc.cvtColor(edgesDetected, edgesDetectedRGB, Imgproc.COLOR_GRAY2BGR);
201 // Probabilistic Line Transform
202 Mat linesP = new Mat(); // will hold the results of the detection
203 double minLineLength = edgesDetectedRGB.size().width/8;
204 Imgproc.HoughLinesP(edgesDetected, linesP, 1, Math.PI / 720, HOUGHLINEP_THRESHOLD, minLineLength, MAXLINEGAP);
205 // Draw the lines
206
207 for (int x = 0; x < linesP.rows(); x++) {
208 double[] l = linesP.get(x, 0);
209 //New angles
210 Point p1 = new Point(l[0], l[1]);
211 Point p2 = new Point(l[2], l[3]);
212 double m = Math.abs(p2.y - p1.y)/(p2.x - p1.x);
213 //System.out.println(l[0]);
214 //System.out.println(l[1]);
215 //System.out.println(l[2]);
216 //System.out.println(l[3]);
217 if(m<SLOPEGRADIENT) {
218 //System.out.println("m: " + m);
219 //Imgproc.line(edgesDetectedRGB, new Point(l[0], l[1]), new Point(l[2], l[3]), new Scalar(0, 0, 255), 1, Imgproc.LINE_AA, 0); SINCE NOT SAVING IMAGES OR COUNTING PIXELS
220 horizontalLineCount++;
221 }
222 }
223
224 //Convert MAT into a BufferedImage
225 //BufferedImage toBeClassifiedImg = toBufferedImage(edgesDetectedRGB); SINCE NOT SAVING IMAGES OR COUNTING PIXELS
226 //Calculate if its sheet music or not
227 isSheetMusic = classifier_HoughLinesP_refined(horizontalLineCount);
228 returnArray.add(isSheetMusic);
229 returnArray.add(horizontalLineCount);
230
231 }
232 catch(Exception e){
233 System.err.println(e);
234 }
235 return returnArray;
236 }
237 private static boolean classifier_HoughLinesP_refined(int lineCount){
238 if(lineCount>MINLINECOUNT){
239 return true;
240 }
241 else{
242 return false;
243 }
244 }
245
246// private static String setup_Cluster(String filename){
247// //NEED TO ADD IMAGE PROC, BEFORE THIS PART BELOW (ANALYSIS OF RESULTS)
248// String returnString = "";
249// //ArrayList lineClusterResult = ClassifierLineClusterPt(linePointsArray, clustersFoundRGB);
250// if(ClassifierLineCount(lineCount) == true){
251// returnString = "LineCount classifier Successful: " + '\t' +"LinesFound: " + lineCount;
252// return returnString;
253// }
254// else if(lineClusterResult.get(0).toString() == "true"){
255// returnString = "LineCluster classifier Successful: " + '\t' + "LinesFound: " + lineCount + '\t' + "ClustersFound: " + lineClusterResult.get(1);
256// return returnString;
257// }
258// return returnString;
259// }
260
261 //******************
262 //INTERNAL FUNCTIONS
263 //******************
264
265 private static BufferedImage toBufferedImage(Mat mat){
266 //MOSTLY COPY PASTE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
267 //MOSTLY COPY PASTE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
268 //https://riptutorial.com/opencv/example/21963/converting-an-mat-object-to-an-bufferedimage-object
269 try{
270
271 int type = BufferedImage.TYPE_3BYTE_BGR;
272 int bufferSize = mat.channels() * mat.cols() * mat.rows();
273 byte[] b = new byte[bufferSize];
274 //get all the pixels
275 mat.get(0, 0, b);
276 BufferedImage image = new BufferedImage(mat.cols(), mat.rows(), type);
277 final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
278 System.arraycopy(b, 0, targetPixels, 0, b.length);
279 return image;
280 }
281 catch(Exception e){
282 System.err.println(e);
283 }
284 return null;
285 }
286 private static ArrayList ClassifierLineClusterPt(ArrayList<StartAndEndPoint> linePointsArray, Mat clustersFoundRGB){
287 /*
288 ADDITION:
289 This will check for a cluster of lines that are close together.
290 1. Go through the list of Y positions(start point) in parsed array.
291 If, there is a small distance between them,
292 then, add to closeLineArray.
293
294 Have all Y positions that are close to each other now.
295 Need to find the lines that are clustered together.
296
297 Now check if there are four of these are close to each other.
298 2. Go through list of closeLine.
299 Get first four lines, traversing down a step each iteration {0,1,2,3} -> {1,2,3,4} -> {2,3,4,5}
300 If, those 4 lines are close together,
301 Then, add them to a new array that holds Line Cluster Values.
302 Go to line 4 positions down since, as do not want duplicates.
303
304 3.
305 */
306 ArrayList returnArray = new ArrayList();
307 ArrayList<StartAndEndPoint> closeLinePts = new ArrayList();
308 ArrayList<StartAndEndPoint[]> clusterPtArray = new ArrayList();
309 int clusterCount = 0;
310 try {
311 if(linePointsArray.size()> 1) {
312 /*
313 //Display input array TESTING PURPOSES
314 for (int i = 0; i < linePointsArray.size(); i++) {
315 System.out.println(linePointsArray.get(i).toString());
316 }
317 */
318 //1. Check if y points are close together
319 //go thru list and compare values against each other
320 for (int i = 0; i < linePointsArray.size(); i++){
321 //System.out.println("i: "+ linePointsArray.get(i).getP1().y);
322 for (int j = 0; j < linePointsArray.size(); j++) {
323 //System.out.println("j: "+ linePointsArray.get(j).getP1().y);
324 //Check if difference is less than 4 and the values are not duplicates.
325 if(Math.abs(linePointsArray.get(j).getP1().y - linePointsArray.get(i).getP1().y) < 5){
326 if(linePointsArray.get(j).getP1().y != linePointsArray.get(i).getP1().y){
327 closeLinePts.add(linePointsArray.get(i));
328 }
329 }
330 }
331 }
332
333 //2. Now check if there are four of these are close to each other.
334 //Go through all of the items in this list and check if four of them are close together
335 //Check first four items, traverse down a step {0,1,2,3} -> {1,2,3,4} -> {2,3,4,5}
336 //If 4 items are close together,
337 //Then add them to a new array that holds Line Cluster Values.
338 //Go down 4 positions down since, as do not want duplicates.
339
340 //Now have an array of at least four lines that are close together.
341 //Sort array and remove duplicates
342 Collections.sort(closeLinePts, new Comparator<StartAndEndPoint>() {
343 @Override
344 public int compare(StartAndEndPoint p1, StartAndEndPoint p2) {
345 return (int)(p1.getP1().y - p2.getP1().y);
346 }
347 });
348 closeLinePts = removeDuplicates(closeLinePts);
349 //DISPLAYING AS EXCEPTED! WOO!
350 /*for (StartAndEndPoint pt : closeLinePts) {
351 System.out.println("CloseLinePTs: " + pt.getP1().y);
352 }*/
353
354
355 if(closeLinePts.size() >= 4) {
356 //FOR every item in array of CloseLines
357 for(int i= 0; i < closeLinePts.size(); i++){
358 //If last comparator is at end of array.
359 if(i + 4 >= closeLinePts.size()){
360 break;
361 }
362 else{
363 //Add 4 values of CloseLinePt Array to a tempArray
364 StartAndEndPoint[] tempPtArray = new StartAndEndPoint[4];
365 tempPtArray[0] = closeLinePts.get(i);
366 tempPtArray[1] = closeLinePts.get(i + 1);
367 tempPtArray[2] = closeLinePts.get(i + 2);
368 tempPtArray[3] = closeLinePts.get(i + 3);
369
370 //Check standard deviation between these 4 values.
371 //If it SD is less than 5 then it is considered to be a cluster of lines.
372 if(ClusterCheck(tempPtArray)){
373 //System.out.println("tempArray PT: "+tempPtArray[0] + " , " + tempPtArray[1] + " , " + tempPtArray[2] + " , " + tempPtArray[3]);
374 //Store array
375 clusterPtArray.add(tempPtArray);
376 //If I + 4 is less than the size of the array then increment by 4
377 //Go down +4 positions in closeLineYPos array
378 if((i + 4 < closeLinePts.size())){
379 //System.out.println("IF, i = " + i + " -> "+ (i+4) + ", CloseLineYpos size= " + closeLineYPos.size());
380 i = i+4;
381 }
382 else{
383 //break
384 Thread.sleep(2000);
385 //System.out.println("End of closeLinePts -> break , i = " + i+ " closeLineYpos size= " + closeLinePts.size());
386 break;
387 }
388 }
389 }
390 }
391 }
392
393 //System.out.println("Cluster Coordinates: ");
394 //for(StartAndEndPoint[] items : clusterPtArray){
395 // for(int i = 0; i <clusterPtArray.size(); i++){
396 // System.out.println("ITEMS: "+ items);
397 // }
398 //}
399
400 //Setup Drawing clusters found.
401 //For every pt given the input array
402 for(StartAndEndPoint pt : linePointsArray){
403 //Go through every the Arrays in the clusterArray(clustered lines)
404 for(int i =0; i < clusterPtArray.size(); i++){
405 //Go through every item in the array
406 for(StartAndEndPoint item : clusterPtArray.get(i)) {
407 //Check if the curr item is equal to current pt
408 if (item.getP1().y == pt.getP1().y){
409 //calculate a different colour for each line
410 //Draw a line
411 Imgproc.line(clustersFoundRGB, pt.getP1(), pt.getP2(), new Scalar(0, 255, 0), 1, Imgproc.LINE_4, 0);
412 }
413 }
414 }
415 }
416
417 clusterCount = clusterPtArray.size();
418 //SETUP RETURN ARRAY
419 if(clusterCount >= 1){
420 returnArray.add(true);
421 returnArray.add(clusterCount);
422 returnArray.add(clustersFoundRGB);
423 }
424 else{
425 returnArray.add(false);
426 returnArray.add(clusterCount);
427 returnArray.add(clustersFoundRGB);
428 }
429 }
430 }
431 catch (Exception e) {
432 System.err.println(e.getMessage());
433 }
434 return returnArray;
435 }
436 private static boolean ClusterCheck(StartAndEndPoint parseArray[]){
437 try {
438 //System.out.println("LENGTH: " + parseArray.length);
439 //MAKE THREE COMPARISONS
440 //After clusters have been found.
441 //Check if their x positions intersect
442 //Logic being
443 //(L1.S < L2.E && L1.S > L2.S)
444 //or
445 //(L2.S < L1.E && L2.S > L1.S)
446 //Variance is using Start of line point.
447 //USING VARIANTS
448 double variance = VarianceCalc(parseArray);
449 Boolean consistent = false;
450 if (variance <= CLUSTER_DISTANCE_MAX && variance > CLUSTER_DISTANCE_MIN) {
451
452 for (int i = 0; i < parseArray.length - 1; i++) {
453 //System.out.println(i);
454 double l1_S = parseArray[i].getP1().x;
455 double l1_E = parseArray[i].getP2().x;
456 double l2_S = parseArray[i + 1].getP1().x;
457 double l2_E = parseArray[i + 1].getP2().x;
458
459 //Check which starts after
460 if (l1_S >= l2_S) {
461 //baseLineStart is l1_S (call with lineComparison)
462 consistent = lineComparison(l1_S, l2_S, l2_E);
463 } else if (l2_S > l1_S) {
464 //baseLineStart is l2_S (call with lineComparison)
465 consistent = lineComparison(l2_S, l1_S, l1_E);
466 } else {
467 System.err.println("An error, comparing l1_S and l2_S, has occurred");
468 }
469
470 //Check if false was returned;
471 if (consistent == false) {
472 /*System.out.print(" X positions of two lines did not overlap each other:" + '\t');
473 System.out.print("l1_S: " + l1_S + '\t');
474 System.out.print("l1_E: " + l1_E + '\t');
475 System.out.print("l2_S: " + l2_S + '\t');
476 System.out.print("l2_E: " + l2_E);
477 System.out.println(" ");*/
478 return false;
479 }
480 }
481 //Have been through for loop, maintaining consistent being true.
482 //Have also meet the variance MIN and MAX requirement. Therefore it is a cluster
483 return true;
484 }
485 //System.out.println("Did not meet Cluster Distance Min and Max requirements, Variance = " + variance);
486 return false;
487 }
488 catch (Exception e){
489 System.err.println(" "+e.getMessage());
490 return false;
491 }
492 }
493 private static double VarianceCalc(StartAndEndPoint parseArray[]){
494 double sum =0;
495 double temp =0;
496 double mean, variance;
497 int size = parseArray.length;
498 //Calculate sum of array
499 for(int i =0; i < parseArray.length; i++){
500 sum += parseArray[i].getP1().y;
501 }
502 //Calculate mean of array
503 mean = sum/parseArray.length;
504 //Calculate variants
505 for(int i =0; i < size; i++){
506 temp += Math.pow((parseArray[i].getP1().y-mean),2);
507 }
508 variance = Math.abs(temp/(size -1));
509 //System.out.println("VARIANCE: " + variance);
510 return variance;
511 }
512 private static boolean lineComparison(double baseLineS, double compareLineS, double compareLineE ){
513 //System.out.print("Comparing baseLineS: " + baseLineS + " with compareLineE: " + compareLineE + " and compareLineS: " + compareLineS);
514 if(baseLineS < compareLineE && baseLineS > compareLineS){
515 return true;
516 }
517 return false;
518 }
519 private static <T> ArrayList<T> removeDuplicates(ArrayList<T> list) {
520 //DIRECTLY COPIED//DIRECTLY COPIED//DIRECTLY COPIED//DIRECTLY COPIED//DIRECTLY COPIED//DIRECTLY COPIED
521 // Function to remove duplicates from an ArrayList
522 // Create a new ArrayList
523 ArrayList<T> newList = new ArrayList();
524 // Traverse through the first list
525 for (T element : list) {
526 // If this element is not present in newList
527 // then add it
528 if (!newList.contains(element)) {
529 newList.add(element);
530 }
531 }
532 // return the new list
533 return newList;
534 //DIRECTLY COPIED//DIRECTLY COPIED//DIRECTLY COPIED//DIRECTLY COPIED//DIRECTLY COPIED//DIRECTLY COPIED
535 }
536 /*private static boolean LineCountOrCluster(int lineCount, ArrayList<StartAndEndPoint> linePointsArray, Mat clustersFoundRGB){
537 ArrayList lineClusterResult = ClassifierLineClusterPt(linePointsArray, clustersFoundRGB);
538
539
540 //String test = ClassifierLineClusterPt(linePointsArray, clustersFoundRGB).get(0).toString();
541 if(ClassifierLineCount(lineCount) == true){
542 System.out.println("LineCount classifier Successful: " + '\t' +"LinesFound: " + lineCount);
543 return true;
544 }
545 else if(lineClusterResult.get(0).toString() == "true"){
546 System.out.println("LineCluster classifier Successful: " + '\t' + "LinesFound: " + lineCount + '\t' + "ClustersFound: " + lineClusterResult.get(1));
547
548 return false;
549 }
550 return false;
551 }*/
552
553}
Note: See TracBrowser for help on using the repository browser.