Edge detection in Java

This is my situation. This includes alignment of the scanned image, which will allow for incorrect scanning. I have to align the scanned image with my Java program.

This is more detailed information:

  • A tabular form is printed on a sheet of paper, which will be scanned into an image file.
  • I will open the picture with Java and I will have OVERLAY text fields.
  • Text fields should align correctly with the scanned image.
  • In order to align correctly, my Java program must analyze the scanned image and determine the coordinates of the edges of the table on the scanned image and, thus, place the image and text fields so that the text fields and the image match correctly (in case of incorrect scanning)

You see, the guy viewing the image may not necessarily place the image in the absolutely correct position, so I need my program to automatically align the scanned image when it is downloaded. This program will be reused for many of these scanned images, so I need the program to be flexible.

My question is one of the following:

  • How can I use Java to determine the y coordinate of the top edge of the table and the x-coordinate of the very left edge of the table. A table is a regular table with many cells with a black thin border printed on a white sheet of paper (horizontal printout).

  • If there is an easier way to automatically align the scanned image so that all the scanned images display a graphic table at the same x, y coordinates, then pass this method :).

  • If you do not know the answer to the above questions, tell me where I should start. I know little about graphical java programming, and I have about 1 month to complete this program. Just suppose I have a tight schedule and I have to make the graphic part as simple as possible for me.

Welcome and thank you.

+4
source share
4 answers

I explored libraries, but in the end I found it more convenient to code my own border detection methods.

In the class below, the black / gray edges of the scanned sheet of paper containing such edges will be detected and the x and y coordinates of the edges of the sheet of paper will be returned, starting from the rightmost end (reverse = true) or from the lower end (reverse = true) or from the upper edges (reverse = false) or from the left edge (reverse = false). Also ... the program will accept ranges along the vertical edges (rangex), measured in pixels, and horizontal ranges (range), measured in pixels. Ranges determine deviations at the received points.

The program performs 4 vertical sections using the indicated arrays and 4 horizontal sections. It extracts the values ​​of the dark dots. It uses ranges to eliminate emissions. Sometimes a small spot on the paper can cause an ejection point. The smaller the range, the less emissions. However, sometimes the edge is slightly inclined, so you do not want the range to be too small.

Enjoy. It works great for me.

import java.awt.image.BufferedImage; import java.awt.Color; import java.util.ArrayList; import java.lang.Math; import java.awt.Point; public class EdgeDetection { public App ap; public int[] horizontalCuts = {120, 220, 320, 420}; public int[] verticalCuts = {300, 350, 375, 400}; public void printEdgesTest(BufferedImage image, boolean reversex, boolean reversey, int rangex, int rangey){ int[] mx = horizontalCuts; int[] my = verticalCuts; //you are getting edge points here //the "true" parameter indicates that it performs a cut starting at 0. (left edge) int[] xEdges = getEdges(image, mx, reversex, true); int edgex = getEdge(xEdges, rangex); for(int x = 0; x < xEdges.length; x++){ System.out.println("EDGE = " + xEdges[x]); } System.out.println("THE EDGE = " + edgex); //the "false" parameter indicates you are doing your cut starting at the end (image.getHeight) //and ending at 0 //if the parameter was true, it would mean it would start the cuts at y = 0 int[] yEdges = getEdges(image, my, reversey, false); int edgey = getEdge(yEdges, rangey); for(int y = 0; y < yEdges.length; y++){ System.out.println("EDGE = " + yEdges[y]); } System.out.println("THE EDGE = " + edgey); } //This function takes an array of coordinates...detects outliers, //and computes the average of non-outlier points. public int getEdge(int[] edges, int range){ ArrayList<Integer> result = new ArrayList<Integer>(); boolean[] passes = new boolean[edges.length]; int[][] differences = new int[edges.length][edges.length-1]; //THIS CODE SEGMENT SAVES THE DIFFERENCES BETWEEN THE POINTS INTO AN ARRAY for(int n = 0; n<edges.length; n++){ for(int m = 0; m<edges.length; m++){ if(m < n){ differences[n][m] = edges[n] - edges[m]; }else if(m > n){ differences[n][m-1] = edges[n] - edges[m]; } } } //This array determines which points are outliers or nots (fall within range of other points) for(int n = 0; n<edges.length; n++){ passes[n] = false; for(int m = 0; m<edges.length-1; m++){ if(Math.abs(differences[n][m]) < range){ passes[n] = true; System.out.println("EDGECHECK = TRUE" + n); break; } } } //Create a new array only using valid points for(int i = 0; i<edges.length; i++){ if(passes[i]){ result.add(edges[i]); } } //Calculate the rounded mean... This will be the x/y coordinate of the edge //Whether they are x or y values depends on the "reverse" variable used to calculate the edges array int divisor = result.size(); int addend = 0; double mean = 0; for(Integer i : result){ addend += i; } mean = (double)addend/(double)divisor; //returns the mean of the valid points: this is the x or y coordinate of your calculated edge. if(mean - (int)mean >= .5){ System.out.println("MEAN " + mean); return (int)mean+1; }else{ System.out.println("MEAN " + mean); return (int)mean; } } //this function computes "dark" points, which include light gray, to detect edges. //reverse - when true, starts counting from x = 0 or y = 0, and ends at image.getWidth or image.getHeight() //verticalEdge - determines whether you want to detect a vertical edge, or a horizontal edge //arr[] - determines the coordinates of the vertical or horizontal cuts you will do //set the arr[] array according to the graphical layout of your scanned image //image - this is the image you want to detect black/white edges of public int[] getEdges(BufferedImage image, int[] arr, boolean reverse, boolean verticalEdge){ int red = 255; int green = 255; int blue = 255; int[] result = new int[arr.length]; for(int n = 0; n<arr.length; n++){ for(int m = reverse ? (verticalEdge ? image.getWidth():image.getHeight())-1:0; reverse ? m>=0:m<(verticalEdge ? image.getWidth():image.getHeight());){ Color c = new Color(image.getRGB(verticalEdge ? m:arr[n], verticalEdge ? arr[n]:m)); red = c.getRed(); green = c.getGreen(); blue = c.getBlue(); //determine if the point is considered "dark" or not. //modify the range if you want to only include really dark spots. //occasionally, though, the edge might be blurred out, and light gray helps if(red<239 && green<239 && blue<239){ result[n] = m; break; } //count forwards or backwards depending on reverse variable if(reverse){ m--; }else{ m++; } } } return result; } } 
0
source

Try to start with a simple scenario and then improve the approach.

  • Angle Detection.
  • Find the angles within the boundaries of the form.
  • Using the coordinates of the corners of the form, calculate the angle of rotation.
  • Rotate / zoom the image.
  • Compare the position of each field in the form relative to the coordinates of the origin.
  • Match the text fields.

The program presented at the end of this post follows steps 1 through 3. It was implemented using the Marvin Framework . The image below shows the output image with the detected angles.

enter image description here

The program also displays: Rotation angle: 1.6365770416167182

Source:

 import java.awt.Color; import java.awt.Point; import marvin.image.MarvinImage; import marvin.io.MarvinImageIO; import marvin.plugin.MarvinImagePlugin; import marvin.util.MarvinAttributes; import marvin.util.MarvinPluginLoader; public class FormCorners { public FormCorners(){ // Load plug-in MarvinImagePlugin moravec = MarvinPluginLoader.loadImagePlugin("org.marvinproject.image.corner.moravec"); MarvinAttributes attr = new MarvinAttributes(); // Load image MarvinImage image = MarvinImageIO.loadImage("./res/printedForm.jpg"); // Process and save output image moravec.setAttribute("threshold", 2000); moravec.process(image, null, attr); Point[] boundaries = boundaries(attr); image = showCorners(image, boundaries, 12); MarvinImageIO.saveImage(image, "./res/printedForm_output.jpg"); // Print rotation angle double angle = (Math.atan2((boundaries[1].y*-1)-(boundaries[0].y*-1),boundaries[1].x-boundaries[0].x) * 180 / Math.PI); angle = angle >= 0 ? angle : angle + 360; System.out.println("Rotation angle:"+angle); } private Point[] boundaries(MarvinAttributes attr){ Point upLeft = new Point(-1,-1); Point upRight = new Point(-1,-1); Point bottomLeft = new Point(-1,-1); Point bottomRight = new Point(-1,-1); double ulDistance=9999,blDistance=9999,urDistance=9999,brDistance=9999; double tempDistance=-1; int[][] cornernessMap = (int[][]) attr.get("cornernessMap"); for(int x=0; x<cornernessMap.length; x++){ for(int y=0; y<cornernessMap[0].length; y++){ if(cornernessMap[x][y] > 0){ if((tempDistance = Point.distance(x, y, 0, 0)) < ulDistance){ upLeft.x = x; upLeft.y = y; ulDistance = tempDistance; } if((tempDistance = Point.distance(x, y, cornernessMap.length, 0)) < urDistance){ upRight.x = x; upRight.y = y; urDistance = tempDistance; } if((tempDistance = Point.distance(x, y, 0, cornernessMap[0].length)) < blDistance){ bottomLeft.x = x; bottomLeft.y = y; blDistance = tempDistance; } if((tempDistance = Point.distance(x, y, cornernessMap.length, cornernessMap[0].length)) < brDistance){ bottomRight.x = x; bottomRight.y = y; brDistance = tempDistance; } } } } return new Point[]{upLeft, upRight, bottomRight, bottomLeft}; } private MarvinImage showCorners(MarvinImage image, Point[] points, int rectSize){ MarvinImage ret = image.clone(); for(Point p:points){ ret.fillRect(px-(rectSize/2), py-(rectSize/2), rectSize, rectSize, Color.red); } return ret; } public static void main(String[] args) { new FormCorners(); } } 
+3
source

Edge detection is what is usually done by enhancing the contrast between adjacent pixels, so you get an easily detectable line that is suitable for further processing.

To do this, the kernel converts the pixel to its pixel value and the value of neighboring pixels. A good edge detection core will improve the differences between adjacent pixels and reduce the strength of a pixel with similar neighbors.

I would start by looking at the Sobel operator. This may not return results that were immediately useful to you; nevertheless, it will bring you closer to you much more than you would like if you had a problem with a little knowledge of the field.

Once you have clear, clean edges, you can use larger kernels to detect points where it seems like 90% bending occurs in two lines, which can give you the pixel coordinates of the outer rectangle, which may be enough for your purposes.

With these external coordinates, it’s still a little mathematically that the new pixels are composted with the average values ​​between the old pixels rotated and moved to match. Results (especially if you don’t know the math of smoothing) can be pretty bad by adding image blur.

Sharpening filters can be a solution, but they have their own problems, basically they make the image sharper by adding grain. Too much, and it is obvious that the original image is not a high-quality scan.

+1
source

A similar similar problem that I did in the past basically figured out the orientation of the form, re-aligned it, re-scaled, and I was tuned. You can use the Hough transform to detect the angular displacement of the image (i.e.: how much it is rotated), but you still need to determine the boundaries of the form. He also had to consider the boundaries of the sheet of paper itself.

It was a good break for me because it basically showed a black and white image in the middle of a large black border.

  • Use an aggressive 5x5 middle filter to eliminate some noise.
  • Convert from shades of gray to black and white (scale the intensity values ​​from [0.255] to [0.1]).
  • Compute the analysis of the main components (i.e. calculate the eigenvectors of the covariance matrix for your image from the calculated eigenvalues) ( http://en.wikipedia.org/wiki/Principal_component_analysis#Derivation_of_PCA_using_the_covariance_method ) 4) This gives you a basic vector. You simply use this to redirect your image to a standard matrix (ie, [1,0], [0,1]).

Your image is now aligned beautifully. I did this to normalize the orientation of the MRI scan of the entire human brain.

You also know that you have a massive black frame around the actual image. You just keep deleting the lines above and below and both sides of the image until they disappear. You can temporarily apply a median or a 7x7 filter to the current image copy. This helps to eliminate too large a border remaining in the final image from prints, dirt, etc.

+1
source

Source: https://habr.com/ru/post/1502699/


All Articles