Java tutorial
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package pipeline; import java.io.*; import com.sun.image.codec.jpeg.*; import java.awt.image.*; import java.util.*; import javax.imageio.ImageIO; import org.opencv.core.Core; import org.opencv.core.CvType; import org.opencv.core.Mat; import org.opencv.core.Rect; /** * Two dimensional box */ class TextRegion { int x1; int y1; int x2; int y2; double mass; /** * Creates a new <code>TextRegion</code> instance. * * @param xs an <code>int</code> value * @param ys an <code>int</code> value * @param xe an <code>int</code> value * @param ye an <code>int</code> value * @param maxx an <code>int</code> value * @param maxy an <code>int</code> value */ TextRegion(int xs, int ys, int xe, int ye, int maxx, int maxy, double m) { if (xs < 0) x1 = 0; else if (xs > maxx) x1 = maxx; else x1 = xs; if (xe < 0) x2 = 0; else if (xe > maxx) x2 = maxx; else x2 = xe; if (ys < 0) y1 = 0; else if (ys > maxy) y1 = maxy; else y1 = ys; if (ye < 0) y2 = 0; else if (ye > maxy) y2 = maxy; else y2 = ye; mass = m; } int area() { return width() * height(); } int height() { return y2 - y1; } int width() { return x2 - x1; } double density() { return mass / area(); } double aspect() { return (double) height() / (double) width(); } } /** * Get text from images * @author <a href="http://www.abstractnonsense.com">Dr. William Bland</a> * @version 1.0 */ public class TextRecognition { private BufferedImage image; /** * Default constructor * @param img The image containing text, fare tuning dei parametri */ public TextRecognition(BufferedImage img) { image = img; merge_densityFactor = 0.5; merge_mass = 15; merge_dist1 = 4; merge_distfac = 1; merge_dist2 = 20; } /** * Constructor for testing purposes */ public TextRecognition(BufferedImage img, double m_densityFactor, int m_mass, int m_dist1, double m_distfac, int m_dist2) { image = img; merge_densityFactor = m_densityFactor; merge_mass = m_mass; merge_dist1 = m_dist1; merge_distfac = m_distfac; merge_dist2 = m_dist2; } /** * Only for debugging - prints out the current parameters */ public void print() { System.out.println("m_densityFactor = " + merge_densityFactor); System.out.println("m_mass = " + merge_mass); System.out.println("m_dist1 = " + merge_dist1); System.out.println("m_distfac = " + merge_distfac); System.out.println("m_dist2 = " + merge_dist2); } int red(int rgb) { return (rgb & 0xff0000) >> 16; } int green(int rgb) { return (rgb & 0x00ff00) >> 8; } int blue(int rgb) { return rgb & 0xff; } int rgb(int red, int green, int blue) { return blue + (green << 8) + (red << 16); } /** * Discard boxes that do not appear to contain text */ LinkedList discardNonText(LinkedList boxes, int[][] contrast) { int i = 0; while (i < boxes.size()) { int numberOfStems = 0; TextRegion thisBox = (TextRegion) boxes.get(i); // Count the stems in this box if (thisBox.y1 != thisBox.y2) { for (int a = thisBox.x1 + 1; a < thisBox.x2 - 1; a++) { int thisStemHeight = 0; for (int b = thisBox.y1 + 1; b < thisBox.y2 - 1; b++) if ((contrast[a][b] != 0 || contrast[a - 1][b] != 0 || contrast[a + 1][b] != 0) && (contrast[a][b - 1] != 0 || contrast[a - 1][b - 1] != 0 || contrast[a + 1][b - 1] != 0) && (contrast[a][b + 1] != 0 || contrast[a - 1][b + 1] != 0 || contrast[a + 1][b + 1] != 0)) thisStemHeight++; //a stem must cover at least 70% of a vertical line if ((100 * thisStemHeight) / thisBox.height() > 70) numberOfStems++; } } if (thisBox.area() < 50 || thisBox.aspect() > .2 || thisBox.height() < 5 || thisBox.width() < 20 // expect at least one stem for every <height> of <width> || numberOfStems < thisBox.width() / thisBox.height()) boxes.remove(i--); i++; } return (boxes); } /** * Shrink each box as much as possible */ LinkedList shrink(LinkedList boxes, int[][] contrast) { int i = 0; while (i < boxes.size()) { TextRegion thisBox = (TextRegion) boxes.get(i); if (thisBox.x1 != thisBox.x2 && thisBox.y1 != thisBox.y2) { int total = 0; for (int a = thisBox.x1; a < thisBox.x2; a++) for (int b = thisBox.y1; b < thisBox.y2; b++) total += contrast[a][b]; double averagex = total / thisBox.height(); double averagey = total / thisBox.width(); int newx1 = thisBox.x1; int newx2 = thisBox.x2; int newy1 = thisBox.y1; int newy2 = thisBox.y2; boolean moved = true; while (newx1 < newx2 && moved) { moved = false; int t1 = 0, t2 = 0; for (int b = thisBox.y1; b < thisBox.y2; b++) { t1 += contrast[newx1][b]; t2 += contrast[newx2][b]; } if (t1 < averagey) { newx1++; moved = true; } if (t2 < averagey) { newx2--; moved = true; } } moved = true; while (newy1 < newy2 && moved) { moved = false; int t1 = 0, t2 = 0; for (int a = thisBox.x1; a < thisBox.x2; a++) { t1 += contrast[a][newy1]; t2 += contrast[a][newy2]; } if (t1 < averagex) { newy1++; moved = true; } if (t2 < averagex) { newy2--; moved = true; } } thisBox.x1 = newx1; thisBox.x2 = newx2; thisBox.y1 = newy1; thisBox.y2 = newy2; } i++; } return (boxes); } public double merge_densityFactor; public int merge_mass; public int merge_dist1; public double merge_distfac; public int merge_dist2; LinkedList merge(LinkedList boxes) { boolean change = true; while (change == true) { change = false; int i = 0; while (i < boxes.size()) { int j = 0; while (i < boxes.size() && j < boxes.size()) { if (i != j) { TextRegion thisBox = (TextRegion) boxes.get(i); TextRegion thatBox = (TextRegion) boxes.get(j); change = merge(thisBox, thatBox); if (change) { boxes.set(i, thisBox); boxes.remove(j); j--; } } j++; } i++; } } return (boxes); } boolean merge(TextRegion thisBox, TextRegion thatBox) { int mergex1 = Math.min(thisBox.x1, thatBox.x1); int mergex2 = Math.max(thisBox.x2, thatBox.x2); int mergey1 = Math.min(thisBox.y1, thatBox.y1); int mergey2 = Math.max(thisBox.y2, thatBox.y2); double mergemass = thisBox.mass + thatBox.mass; double mergedensity = mergemass / ((mergex2 - mergex1) * (mergey2 - mergey1)); double mergeaspect = ((double) mergey2 - mergey1) / ((double) mergex2 - mergex1); double reasonsToMerge = 0; if (mergedensity > merge_densityFactor * thisBox.density()) reasonsToMerge++; if (mergedensity > merge_densityFactor * thatBox.density()) reasonsToMerge++; if (mergeaspect < thisBox.aspect()) reasonsToMerge++; if (mergeaspect < thatBox.aspect()) reasonsToMerge++; if (thisBox.mass > merge_mass && thatBox.mass > merge_mass) reasonsToMerge++; int maxboxwidth = Math.max(thisBox.width(), thatBox.width()); if (Math.abs(thisBox.y1 - thatBox.y1) < merge_dist1 && Math.abs(thisBox.y2 - thatBox.y1) < merge_dist1 && (Math.abs(thisBox.x1 - thatBox.x2) < merge_distfac * maxboxwidth || Math.abs(thisBox.x2 - thatBox.x1) < merge_distfac * maxboxwidth)) reasonsToMerge++; if ((Math.abs(thisBox.y1 - thatBox.y1) < merge_dist2 || Math.abs(thisBox.y2 - thatBox.y2) < merge_dist2) && (Math.abs(thisBox.x1 - thatBox.x2) < merge_distfac * maxboxwidth || Math.abs(thisBox.x2 - thatBox.x1) < merge_distfac * maxboxwidth)) reasonsToMerge++; if (reasonsToMerge > 3) { // 7 reasons max thisBox.x1 = mergex1; thisBox.x2 = mergex2; thisBox.y1 = mergey1; thisBox.y2 = mergey2; thisBox.mass = mergemass; return true; } return false; } int[][] getContrast() { // Find pixels that stand out from the background int[][] contrast = new int[image.getWidth()][image.getHeight()]; int[][] temp = new int[image.getWidth()][image.getHeight()]; for (int i = 2; i < image.getWidth() - 2; i++) for (int j = 2; j < image.getHeight() - 2; j++) { int thisPixel = image.getRGB(i, j); int left = image.getRGB(i - 1, j); int left2 = image.getRGB(i - 2, j); int right = image.getRGB(i + 1, j); int right2 = image.getRGB(i + 2, j); int up = image.getRGB(i, j - 1); int down = image.getRGB(i, j + 1); int t1 = 60; // thresholds int t2 = 80; if (Math.abs(blue(thisPixel) - blue(right)) > t1 || Math.abs(blue(thisPixel) - blue(left)) > t1 || Math.abs(blue(thisPixel) - blue(down)) > t1 || Math.abs(blue(thisPixel) - blue(up)) > t1 || Math.abs(blue(thisPixel) - blue(right2)) > t2 || Math.abs(blue(thisPixel) - blue(left2)) > t2 || Math.abs(green(thisPixel) - green(right)) > t1 || Math.abs(green(thisPixel) - green(left)) > t1 || Math.abs(green(thisPixel) - green(down)) > t1 || Math.abs(green(thisPixel) - green(up)) > t1 || Math.abs(green(thisPixel) - green(right2)) > t2 || Math.abs(green(thisPixel) - green(left2)) > t2 || Math.abs(red(thisPixel) - red(right)) > t1 || Math.abs(red(thisPixel) - red(left)) > t1 || Math.abs(red(thisPixel) - red(down)) > t1 || Math.abs(red(thisPixel) - red(up)) > t1 || Math.abs(red(thisPixel) - red(right2)) > t2 || Math.abs(red(thisPixel) - red(left2)) > t2) temp[i][j] = 1; } // Look for areas of contrast that extend vertically and horizontally // but not too far, to eliminate long straight lines (e.g. borders) for (int j = 2; j < image.getHeight() - 2; j++) for (int i = 2; i < image.getWidth() - 2; i++) if (temp[i][j] == 1) { int width = 0; int height = 0; for (int k = 0; i + k < image.getWidth() - 2 && i - k > 2 && (temp[i + k][j] == 1 || temp[i - k][j] == 1) && width++ < 100; k++) ; for (int k = 0; j + k < image.getHeight() - 2 && j - k > 2 && (temp[i][j + k] == 1 || temp[i][j - k] == 1) && height++ < 100; k++) ; int totalOnLine = 0; for (int k = Math.max(2, i - 40); k < Math.min(image.getWidth() - 2, i + 40); k++) totalOnLine += temp[k][j]; if (totalOnLine > 7 && width < 100 && height < 100) contrast[i][j] = 1; } return contrast; } /** * Looks for areas of text in an image. * @return a LinkedList of boxes that are likely to contain text. */ public LinkedList getTextBoxes() { LinkedList boxes = new LinkedList(); int[][] contrast = getContrast(); try { String nomeFile = "src/pipeline/receivedImg/" + Global.getJPGNameFile() + " contrast.jpg"; FileOutputStream out = new FileOutputStream(nomeFile); JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out); BufferedImage contrastjpg = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB); for (int i = 0; i < image.getWidth(); i++) for (int j = 0; j < image.getHeight(); j++) contrastjpg.setRGB(i, j, 0xffffff * contrast[i][j]); encoder.encode(contrastjpg); out.close(); File tmp = new File(nomeFile); tmp.delete(); } catch (Exception e) { System.out.println("Exception: " + e); } int contrastOnLine[] = new int[image.getHeight()]; for (int j = 1; j < image.getHeight() - 1; j++) { int count = 0; contrastOnLine[j] = 0; for (int a = 0; a < image.getWidth(); a++) { count += contrast[a][j]; contrastOnLine[j] += contrast[a][j]; } } for (int j = 1; j < image.getHeight() - 1; j++) contrastOnLine[j] = (contrastOnLine[j - 1] + contrastOnLine[j] + contrastOnLine[j + 1]) / 3; for (int j = 1; j < image.getHeight() - 1; j++) contrastOnLine[j] = (contrastOnLine[j - 1] + contrastOnLine[j] + contrastOnLine[j + 1]) / 3; int averageOnLine = 0; for (int j = 1; j < image.getHeight() - 1; j++) averageOnLine += contrastOnLine[j]; averageOnLine /= (image.getHeight() - 2); boolean intext = false; int boxstart = 0; int boxaverage = 0; int boxlines = 0; for (int j = 1; j < image.getHeight() - 1; j++) { if (contrastOnLine[j] > averageOnLine && !intext) { intext = true; boxstart = j; boxaverage = contrastOnLine[j]; boxlines = 1; } else if (contrastOnLine[j] > averageOnLine) { boxaverage += contrastOnLine[j]; boxlines++; } else if (contrastOnLine[j] <= averageOnLine && intext) { // found vertical limits, now find horizontal. intext = false; int boxend = j; if (boxend - boxstart > 10) { // text must be higher than 10 pixels boxaverage /= boxlines; int contrastOnColumn[] = new int[image.getWidth()]; for (int i = 1; i < image.getWidth() - 1; i++) for (int b = boxstart; b < boxend; b++) contrastOnColumn[i] += contrast[i][b]; for (int i = 1; i < image.getWidth() - 1; i++) contrastOnColumn[i] = (contrastOnColumn[i - 1] + contrastOnColumn[i] + contrastOnColumn[i + 1]) / 3; for (int i = 1; i < image.getWidth() - 1; i++) contrastOnColumn[i] = (contrastOnColumn[i - 1] + contrastOnColumn[i] + contrastOnColumn[i + 1]) / 3; int averageOnColumn = 0; for (int i = 1; i < image.getWidth() - 1; i++) averageOnColumn += contrastOnColumn[i]; averageOnColumn /= (image.getWidth() - 2); boolean intextx = false; int boxstartx = 0; for (int i = 1; i < image.getWidth() - 1; i++) { if (contrastOnColumn[i] > averageOnColumn / 2 && !intextx) { intextx = true; boxstartx = i; } else if (contrastOnColumn[i] <= averageOnColumn / 2 && intextx) { intextx = false; int boxendx = i; // found horizontal limits, // now (if necessary) shrink // vertical limits int newcount = 0; int tempboxstart = boxstart; int tempboxend = boxend; while (tempboxstart < boxend && newcount == 0) { for (int a = boxstartx; a < boxendx; a++) newcount += contrast[a][tempboxstart]; if (newcount < 2) tempboxstart++; } newcount = 0; while (tempboxstart < boxend && newcount == 0) { for (int a = boxstartx; a < boxendx; a++) newcount += contrast[a][tempboxend]; if (newcount < 2) tempboxend--; } TextRegion thisBox = new TextRegion(boxstartx, tempboxstart, boxendx, tempboxend, image.getWidth(), image.getHeight(), boxaverage); boxes.add(thisBox); } } } } } // System.out.println( boxes.size() + " bounding boxes" ); shrink(boxes, contrast); boxes = merge(boxes); shrink(boxes, contrast); // System.out.println( boxes.size() + " bounding boxes after merge" ); boxes = discardNonText(boxes, contrast); //System.out.println( boxes.size() + " bounding boxes after delete" ); return (shrink(boxes, contrast)); } /** * Isolate text * @return a <code>BufferedImage</code> value */ public BufferedImage isolateText(LinkedList boxes) { BufferedImage outputimage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB); // make everything monochrome for (int a = 0; a < image.getWidth(); a++) for (int b = 0; b < image.getHeight(); b++) { int colour = image.getRGB(a, b); int average = (red(colour) + green(colour) + blue(colour)) / 3; outputimage.setRGB(a, b, rgb(average, average, average)); } // fill text boxes with colour for (int i = 0; i < boxes.size(); i++) { TextRegion thisBox = (TextRegion) boxes.get(i); int x1 = Math.max(1, thisBox.x1); int x2 = Math.min(image.getWidth() - 2, thisBox.x2); int y1 = Math.max(1, thisBox.y1); int y2 = Math.min(image.getHeight() - 2, thisBox.y2); for (int a = x1; a < x2; a++) for (int b = y1; b < y2; b++) outputimage.setRGB(a, b, image.getRGB(a, b)); } // draw red border around each text box int RED = 0xff0000; for (int i = 0; i < boxes.size(); i++) { TextRegion thisBox = (TextRegion) boxes.get(i); int x1 = Math.max(1, thisBox.x1); int x2 = Math.min(image.getWidth() - 2, thisBox.x2); int y1 = Math.max(1, thisBox.y1); int y2 = Math.min(image.getHeight() - 2, thisBox.y2); for (int a = x1; a < x2; a++) { outputimage.setRGB(a, thisBox.y1, RED); outputimage.setRGB(a, thisBox.y2, RED); } for (int a = y1; a < y2; a++) { outputimage.setRGB(thisBox.x1, a, RED); outputimage.setRGB(thisBox.x2, a, RED); } } return (outputimage); } public static String SplitFiles(File fileIn) { String result = ""; try { String nomeFile = fileIn.getName(); //System.out.println("il nome del file "+nomeFile); FileInputStream in = new FileInputStream("src/pipeline/receivedImg/" + nomeFile); JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(in); BufferedImage image = decoder.decodeAsBufferedImage(); in.close(); TextRecognition myget = new TextRecognition(image); LinkedList boxes = myget.getTextBoxes(); String nomeFileOut = "src/pipeline/outputImg/" + Global.getJPGNameFile() + " out.jpg"; FileOutputStream out = new FileOutputStream(nomeFileOut); JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out); encoder.encode(myget.isolateText(boxes)); out.close(); //parte con opencv System.loadLibrary(Core.NATIVE_LIBRARY_NAME); File f = new File("src/pipeline/receivedImg/" + nomeFile); BufferedImage imageFile = ImageIO.read(f); byte[] data = ((DataBufferByte) imageFile.getRaster().getDataBuffer()).getData(); Mat mat = new Mat(imageFile.getHeight(), imageFile.getWidth(), CvType.CV_8UC3); mat.put(0, 0, data); int tolleranza = 15; for (int i = 0; i < boxes.size(); i++) { TextRegion app = (TextRegion) boxes.get(i); // System.out.println("RIGA: "+i+" -> "+app.x1 +" "+app.x2 +" "+app.y1 +" "+app.y2 +" "); Rect roi1 = new Rect(app.x1 - tolleranza, app.y1 - tolleranza, app.x2 - app.x1 + tolleranza, app.y2 - app.y1 + 2 * tolleranza); Mat mat1 = new Mat(mat, roi1); byte[] data1 = new byte[mat1.rows() * mat1.cols() * (int) (mat1.elemSize())]; mat1.get(0, 0, data1); BufferedImage image1 = new BufferedImage(mat1.cols(), mat1.rows(), BufferedImage.TYPE_3BYTE_BGR); image1.getRaster().setDataElements(0, 0, mat1.cols(), mat1.rows(), data1); String nomeFileUscrita = "src/pipeline/outputImg/" + i + Global.getJPGNameFile() + " uscita.jpg"; File tmp = new File(nomeFileUscrita); File output = new File(nomeFileUscrita); ImageIO.write(image1, "jpg", output); result += (i + 1) + ")" + OCR_Processing.performOCR_String2Text(output); tmp.delete(); } f.delete(); File foo = new File(nomeFileOut); foo.delete(); } catch (Exception e) { System.out.println("Exception: " + e); } return result; } }