2010-10-14 14 views
9

मुझे जावा में एक छवि विश्लेषण एल्गोरिदम पर थोड़ी मदद चाहिए। मैं मूल रूप से इस तरह छवियों: alt textजावा छवि विश्लेषण - लंबवत रेखाओं की गणना

तो, जैसा कि आप समझ सकते हैं, मैं लाइनों की गिनती करने की जरूरत है।

आपको कौन सा दृष्टिकोण सबसे अच्छा लगता है?

धन्यवाद, Smaug

+1

http: // en .wikipedia.org/wiki/Hough_transform इससे मदद मिल सकती है, बीटीडब्ल्यू +1 –

उत्तर

1
package ac.essex.ooechs.imaging.commons.edge.hough; 

import java.awt.image.BufferedImage; 
import java.awt.*; 
import java.util.Vector; 
import java.io.File; 

/** 
* <p/> 
* Java Implementation of the Hough Transform.<br /> 
* Used for finding straight lines in an image.<br /> 
* by Olly Oechsle 
* </p> 
* <p/> 
* Note: This class is based on original code from:<br /> 
* <a href="http://homepages.inf.ed.ac.uk/rbf/HIPR2/hough.htm">http://homepages.inf.ed.ac.uk/rbf/HIPR2/hough.htm</a> 
* </p> 
* <p/> 
* If you represent a line as:<br /> 
* x cos(theta) + y sin (theta) = r 
* </p> 
* <p/> 
* ... and you know values of x and y, you can calculate all the values of r by going through 
* all the possible values of theta. If you plot the values of r on a graph for every value of 
* theta you get a sinusoidal curve. This is the Hough transformation. 
* </p> 
* <p/> 
* The hough tranform works by looking at a number of such x,y coordinates, which are usually 
* found by some kind of edge detection. Each of these coordinates is transformed into 
* an r, theta curve. This curve is discretised so we actually only look at a certain discrete 
* number of theta values. "Accumulator" cells in a hough array along this curve are incremented 
* for X and Y coordinate. 
* </p> 
* <p/> 
* The accumulator space is plotted rectangularly with theta on one axis and r on the other. 
* Each point in the array represents an (r, theta) value which can be used to represent a line 
* using the formula above. 
* </p> 
* <p/> 
* Once all the points have been added should be full of curves. The algorithm then searches for 
* local peaks in the array. The higher the peak the more values of x and y crossed along that curve, 
* so high peaks give good indications of a line. 
* </p> 
* 
* @author Olly Oechsle, University of Essex 
*/ 

public class HoughTransform extends Thread { 

    public static void main(String[] args) throws Exception { 
     String filename = "/home/ooechs/Desktop/vase.png"; 

     // load the file using Java's imageIO library 
     BufferedImage image = javax.imageio.ImageIO.read(new File(filename)); 

     // create a hough transform object with the right dimensions 
     HoughTransform h = new HoughTransform(image.getWidth(), image.getHeight()); 

     // add the points from the image (or call the addPoint method separately if your points are not in an image 
     h.addPoints(image); 

     // get the lines out 
     Vector<HoughLine> lines = h.getLines(30); 

     // draw the lines back onto the image 
     for (int j = 0; j < lines.size(); j++) { 
      HoughLine line = lines.elementAt(j); 
      line.draw(image, Color.RED.getRGB()); 
     } 
    } 

    // The size of the neighbourhood in which to search for other local maxima 
    final int neighbourhoodSize = 4; 

    // How many discrete values of theta shall we check? 
    final int maxTheta = 180; 

    // Using maxTheta, work out the step 
    final double thetaStep = Math.PI/maxTheta; 

    // the width and height of the image 
    protected int width, height; 

    // the hough array 
    protected int[][] houghArray; 

    // the coordinates of the centre of the image 
    protected float centerX, centerY; 

    // the height of the hough array 
    protected int houghHeight; 

    // double the hough height (allows for negative numbers) 
    protected int doubleHeight; 

    // the number of points that have been added 
    protected int numPoints; 

    // cache of values of sin and cos for different theta values. Has a significant performance improvement. 
    private double[] sinCache; 
    private double[] cosCache; 

    /** 
    * Initialises the hough transform. The dimensions of the input image are needed 
    * in order to initialise the hough array. 
    * 
    * @param width The width of the input image 
    * @param height The height of the input image 
    */ 
    public HoughTransform(int width, int height) { 

     this.width = width; 
     this.height = height; 

     initialise(); 

    } 

    /** 
    * Initialises the hough array. Called by the constructor so you don't need to call it 
    * yourself, however you can use it to reset the transform if you want to plug in another 
    * image (although that image must have the same width and height) 
    */ 
    public void initialise() { 

     // Calculate the maximum height the hough array needs to have 
     houghHeight = (int) (Math.sqrt(2) * Math.max(height, width))/2; 

     // Double the height of the hough array to cope with negative r values 
     doubleHeight = 2 * houghHeight; 

     // Create the hough array 
     houghArray = new int[maxTheta][doubleHeight]; 

     // Find edge points and vote in array 
     centerX = width/2; 
     centerY = height/2; 

     // Count how many points there are 
     numPoints = 0; 

     // cache the values of sin and cos for faster processing 
     sinCache = new double[maxTheta]; 
     cosCache = sinCache.clone(); 
     for (int t = 0; t < maxTheta; t++) { 
      double realTheta = t * thetaStep; 
      sinCache[t] = Math.sin(realTheta); 
      cosCache[t] = Math.cos(realTheta); 
     } 
    } 

    /** 
    * Adds points from an image. The image is assumed to be greyscale black and white, so all pixels that are 
    * not black are counted as edges. The image should have the same dimensions as the one passed to the constructor. 
    */ 
    public void addPoints(BufferedImage image) { 

     // Now find edge points and update the hough array 
     for (int x = 0; x < image.getWidth(); x++) { 
      for (int y = 0; y < image.getHeight(); y++) { 
       // Find non-black pixels 
       if ((image.getRGB(x, y) & 0x000000ff) != 0) { 
        addPoint(x, y); 
       } 
      } 
     } 
    } 

    /** 
    * Adds a single point to the hough transform. You can use this method directly 
    * if your data isn't represented as a buffered image. 
    */ 
    public void addPoint(int x, int y) { 

     // Go through each value of theta 
     for (int t = 0; t < maxTheta; t++) { 

      //Work out the r values for each theta step 
      int r = (int) (((x - centerX) * cosCache[t]) + ((y - centerY) * sinCache[t])); 

      // this copes with negative values of r 
      r += houghHeight; 

      if (r < 0 || r >= doubleHeight) continue; 

      // Increment the hough array 
      houghArray[t][r]++; 

     } 

     numPoints++; 
    } 

    /** 
    * Once points have been added in some way this method extracts the lines and returns them as a Vector 
    * of HoughLine objects, which can be used to draw on the 
    * 
    * @param percentageThreshold The percentage threshold above which lines are determined from the hough array 
    */ 
    public Vector<HoughLine> getLines(int threshold) { 

     // Initialise the vector of lines that we'll return 
     Vector<HoughLine> lines = new Vector<HoughLine>(20); 

     // Only proceed if the hough array is not empty 
     if (numPoints == 0) return lines; 

     // Search for local peaks above threshold to draw 
     for (int t = 0; t < maxTheta; t++) { 
      loop: 
      for (int r = neighbourhoodSize; r < doubleHeight - neighbourhoodSize; r++) { 

       // Only consider points above threshold 
       if (houghArray[t][r] > threshold) { 

        int peak = houghArray[t][r]; 

        // Check that this peak is indeed the local maxima 
        for (int dx = -neighbourhoodSize; dx <= neighbourhoodSize; dx++) { 
         for (int dy = -neighbourhoodSize; dy <= neighbourhoodSize; dy++) { 
          int dt = t + dx; 
          int dr = r + dy; 
          if (dt < 0) dt = dt + maxTheta; 
          else if (dt >= maxTheta) dt = dt - maxTheta; 
          if (houghArray[dt][dr] > peak) { 
           // found a bigger point nearby, skip 
           continue loop; 
          } 
         } 
        } 

        // calculate the true value of theta 
        double theta = t * thetaStep; 

        // add the line to the vector 
        lines.add(new HoughLine(theta, r)); 

       } 
      } 
     } 

     return lines; 
    } 

    /** 
    * Gets the highest value in the hough array 
    */ 
    public int getHighestValue() { 
     int max = 0; 
     for (int t = 0; t < maxTheta; t++) { 
      for (int r = 0; r < doubleHeight; r++) { 
       if (houghArray[t][r] > max) { 
        max = houghArray[t][r]; 
       } 
      } 
     } 
     return max; 
    } 

    /** 
    * Gets the hough array as an image, in case you want to have a look at it. 
    */ 
    public BufferedImage getHoughArrayImage() { 
     int max = getHighestValue(); 
     BufferedImage image = new BufferedImage(maxTheta, doubleHeight, BufferedImage.TYPE_INT_ARGB); 
     for (int t = 0; t < maxTheta; t++) { 
      for (int r = 0; r < doubleHeight; r++) { 
       double value = 255 * ((double) houghArray[t][r])/max; 
       int v = 255 - (int) value; 
       int c = new Color(v, v, v).getRGB(); 
       image.setRGB(t, r, c); 
      } 
     } 
     return image; 
    } 

} 

स्रोत: http://vase.essex.ac.uk/software/HoughTransform/HoughTransform.java.html

2

एक साधारण विभाजन एल्गोरिथ्म आप मदद कर सकते हैं। यहाँ है कि कैसे एल्गोरिथ्म काम करता है: सही और रिकॉर्ड पहले काला (जो भी आपके लाइन का रंग है) पिक्सेल की स्थिति बाएं से

  • स्कैन पिक्सेल। इस प्रक्रिया पर
  • कैरी जब तक आप एक पूरे स्कैन जब आप काला पिक्सेल नहीं मिल रहा है पाते हैं। इस स्थिति को भी रिकॉर्ड करें।
  • हम सिर्फ वाई में रुचि रखते यहाँ की स्थिति हैं। अब इस वाई स्थिति का उपयोग छवि को क्षैतिज रूप से विभाजित करें।
  • अब हम एक ही प्रक्रिया क्या करने जा रहे लेकिन इस बार हम ऊपर से नीचे तक स्कैन करने के लिए (एक स्तंभ एक समय) खंड हम अभी बनाया में जा रहे हैं।
  • इस बार हम एक्स पदों में रुचि रखते हैं।
  • तो अंत में हम हर लाइनों विस्तार मिलता है या आप हर पंक्ति के लिए सीमांकन बॉक्स कह सकते हैं।
  • इन बाउंडिंग बॉक्स की कुल संख्या लाइनों की संख्या है।

आप अपनी आवश्यकताओं के अनुसार एल्गोरिथ्म में कई अनुकूलन कर सकते हैं।

+0

+1 अच्छा स्पष्टीकरण। यह ओसीआर में चरित्र विभाजन के लिए भी प्रयोग किया जाता है। –

+0

क्या यह प्राप्त करने के लिए जावा एपीआई है? – Sid

0

यह इस बात पर निर्भर करता है कि वे इस तरह कितने दिखते हैं।

  1. एक तरीका है कि लाइनों को बरकरार रखता है और शुद्ध सफेद
  2. शायद दाग हटाने की तरह साधारण सफाई करने के लिए पृष्ठभूमि (किसी भी छोटे काले घटकों निकालने के लिए) लाता में 1-बिट (काले और सफेद) के लिए छवि ले आओ।
फिर

,

  1. एक काले रंग की पिक्सेल का पता लगाएं
  2. उपयोग बाढ़ भरने एल्गोरिदम अपनी हद को खोजने के लिए
  3. देखने के लिए कि आकार एक लाइन होने के लिए मानदंडों को पूरा करती (lineCount ++ यदि ऐसा है तो)
  4. को दूर यह
  5. दोहराएँ इस जब तक वहाँ कोई काला पिक्सल हैं

एक बहुत कितना अच्छा आप # 3, कुछ विचार Hough

  1. उपयोग सिर्फ इस खंड पर जाँच करने के लिए एक पंक्ति है कि ऐसा करने पर निर्भर करता है, और यह ऊर्ध्वाधर (ish)
  2. (# के बाद यह है कि 1) खड़ी करने के लिए इसे बारी बारी से और उसके चौड़ाई/ऊंचाई अनुपात
1

मैं पाता है कि खड़ी लाइनों के प्रारंभ और समाप्ति बिंदुओं और रेखाओं की कुल संख्या प्रिंट एक सरल उपाय (सुधार किया जाना चाहिए) Marvin Framework का उपयोग कर क्रियान्वित किया है की जाँच मिल गया।

दृष्टिकोण:

  1. Binarize छवि एक दी गई सीमा से इस्तेमाल करते हैं।
  2. प्रत्येक पिक्सेल के लिए, यदि यह काला (ठोस), एक खड़ी रेखा
  3. सहेजें एक्स, वाई, प्रारंभ और अंत बिंदु
  4. लाइन एक न्यूनतम लंबाई है की खोजने की कोशिश? यह एक स्वीकार्य रेखा है!
  5. लाल रंग में प्रारंभ बिंदु और हरे रंग के अंत बिंदु को प्रिंट करें।

उत्पादन छवि के नीचे दिखाया गया है:

enter image description here

कार्यक्रमों उत्पादन:

Vertical line fount at: (74,9,70,33) 
Vertical line fount at: (113,9,109,31) 
Vertical line fount at: (80,10,76,32) 
Vertical line fount at: (137,11,133,33) 
Vertical line fount at: (163,11,159,33) 
Vertical line fount at: (184,11,180,33) 
Vertical line fount at: (203,11,199,33) 
Vertical line fount at: (228,11,224,33) 
Vertical line fount at: (248,11,244,33) 
Vertical line fount at: (52,12,50,33) 
Vertical line fount at: (145,13,141,35) 
Vertical line fount at: (173,13,169,35) 
Vertical line fount at: (211,13,207,35) 
Vertical line fount at: (94,14,90,36) 
Vertical line fount at: (238,14,236,35) 
Vertical line fount at: (130,16,128,37) 
Vertical line fount at: (195,16,193,37) 
Vertical lines total: 17 

अंत में, स्रोत कोड:

import java.awt.Color; 
import java.awt.Point; 
import marvin.image.MarvinImage; 
import marvin.io.MarvinImageIO; 
import marvin.plugin.MarvinImagePlugin; 
import marvin.util.MarvinPluginLoader; 

public class VerticalLineCounter { 

    private MarvinImagePlugin threshold = MarvinPluginLoader.loadImagePlugin("org.marvinproject.image.color.thresholding"); 

    public VerticalLineCounter(){ 
     // Binarize 
     MarvinImage image = MarvinImageIO.loadImage("./res/lines.jpg"); 
     MarvinImage binImage = image.clone(); 
     threshold.setAttribute("threshold", 127); 
     threshold.process(image, binImage); 

     // Find lines and save an output image 
     MarvinImage imageOut = findVerticalLines(binImage, image); 
     MarvinImageIO.saveImage(imageOut, "./res/lines_out.png"); 
    } 

    private MarvinImage findVerticalLines(MarvinImage binImage, MarvinImage originalImage){ 
     MarvinImage imageOut = originalImage.clone(); 
     boolean[][] processedPixels = new boolean[binImage.getWidth()][binImage.getHeight()]; 
     int color; 
     Point endPoint; 
     int totalLines=0; 
     for(int y=0; y<binImage.getHeight(); y++){ 
      for(int x=0; x<binImage.getWidth(); x++){ 
       if(!processedPixels[x][y]){ 
        color = binImage.getIntColor(x, y); 

        // Black? 
        if(color == 0xFF000000){ 
         endPoint = getEndOfLine(x,y,binImage,processedPixels); 

         // Line lenght threshold 
         if(endPoint.x - x > 5 || endPoint.y - y > 5){ 
          imageOut.fillRect(x-2, y-2, 5, 5, Color.red); 
          imageOut.fillRect(endPoint.x-2, endPoint.y-2, 5, 5, Color.green); 
          totalLines++; 
          System.out.println("Vertical line fount at: ("+x+","+y+","+endPoint.x+","+endPoint.y+")"); 
         } 
        } 
       } 
       processedPixels[x][y] = true; 
      } 
     } 
     System.out.println("Vertical lines total: "+totalLines); 
     return imageOut; 
    } 

    private Point getEndOfLine(int x, int y, MarvinImage image, boolean[][] processedPixels){ 
     int xC=x; 
     int cY=y; 
     while(true){ 
      processedPixels[xC][cY] = true; 
      processedPixels[xC-1][cY] = true; 
      processedPixels[xC-2][cY] = true; 
      processedPixels[xC-3][cY] = true; 
      processedPixels[xC+1][cY] = true; 
      processedPixels[xC+2][cY] = true; 
      processedPixels[xC+3][cY] = true; 

      if(getSafeIntColor(xC,cY,image) < 0xFF000000){ 
       // nothing 
      } 
      else if(getSafeIntColor(xC-1,cY,image) == 0xFF000000){ 
       xC = xC-2; 
      } 
      else if(getSafeIntColor(xC-2,cY,image) == 0xFF000000){ 
       xC = xC-3; 
      } 
      else if(getSafeIntColor(xC+1,cY,image) == 0xFF000000){ 
       xC = xC+2; 
      } 
      else if(getSafeIntColor(xC+2,cY,image) == 0xFF000000){ 
       xC = xC+3; 
      } 
      else{ 
       return new Point(xC, cY); 
      } 
      cY++; 
     } 
    } 
    private int getSafeIntColor(int x, int y, MarvinImage image){ 
     if(x >= 0 && x < image.getWidth() && y >= 0 && y < image.getHeight()){ 
      return image.getIntColor(x, y); 
     } 
     return -1; 
    } 
    public static void main(String args[]){ 
     new VerticalLineCounter(); 
     System.exit(0); 
    } 
}