2013-03-24 85 views
1

我有一個黑色圓圈的圖像。使用JUI檢測圖像中的黑色圓圈(不僅僅是像素)

該圖像是一份調查表的掃描副本,非常像OMR調查問卷表。

我想檢測已使用燻黑圈JUI(如果需要任何其他API)

我有,而搜索的幾個例子,但他們不給我準確的結果。

我試過.. UDAI,Moodle的 ...等...

然後,我決定讓我自己。我能夠檢測到黑色像素,但如下所示。

BufferedImage mapa = BMPDecoder.read(new File("testjui.bmp")); 

      final int xmin = mapa.getMinX(); 
      final int ymin = mapa.getMinY(); 

      final int ymax = ymin + mapa.getHeight(); 
      final int xmax = xmin + mapa.getWidth(); 


      for (int i = xmin;i<xmax;i++) 
      { 
       for (int j = ymin;j<ymax;j++) 
       { 

       int pixel = mapa.getRGB(i, j); 

       if ((pixel & 0x00FFFFFF) == 0) 
       { 
        System.out.println("("+i+","+j+")"); 
       } 
       } 
      } 

這給了我所有的黑色像素的座標,但我不知道如果它的一個圓或不。

我怎樣才能確定它的一個圓圈。

2]另外我想知道如果掃描的圖像是傾斜的....我知道Udai api照顧,但由於某種原因,我無法讓我的調查模板運行該代碼。

回答

0

您需要在模板中編程一個圓圈的樣子,然後使其可擴展以適合不同的圓圈大小。

例如半徑3圈將是:

o 
ooo 
    o 

這裏假設你有一組有限的,你需要找到,也許多達5x5的6x6的或這是可行的圓圈。

或者您可以使用:Midpoint circle algorithm
這將涉及查找所有黑色像素組,然後爲每個黑色像素組選擇中間像素。
應用此算法使用外部像素作爲guid指定圓的大小。
找出黑色/預期黑色像素之間的差異。
如果黑色與預期的黑色比例足夠高,則會出現黑色圓圈,您可以將其刪除/美白。

4

所以,如果我理解正確,你有代碼挑出黑色像素,所以現在你有所有的黑色像素的座標,你想確定所有落在一個圓上的那些。

我會以這種方式分兩步進行。

1)將像素聚類。創建一個名爲Cluster的類,該類包含一系列點並使用您的聚類算法將所有點放入正確的羣集中。

2)確定哪些羣集是圓圈。要做到這一點,找到每個羣集中所有點的中點(只取所有點的平均值)。然後找到距中心的最小和最大距離,它們之間的差值應小於文件中圓的最大厚度。這些將爲您提供圓圈內最內圈和最外圈的半徑。現在使用圓的方程x^2 + y^2 = radius,將半徑設置爲之前找到的最大值和最小值之間的值,以查找您的羣應包含的點。如果你的集羣包含這些,它就是一個圓圈。

當然還有其他需要考慮的問題是,形狀是否有近似橢圓而不是圓形,在這種情況下,您應該使用橢圓公式。此外,如果您的文件包含圓形形狀,則需要編寫其他代碼以排除這些形狀。另一方面,如果所有圈子的大小完全相同,則可以通過讓算法僅搜索該大小的圓圈來削減需要完成的工作。

我希望我能有一些幫助,祝你好運!

+0

我覺得這個方法應該爲我工作...但你可以建議我在java中的聚類算法,將有助於這個.. – 2013-04-05 05:28:23

+0

據我所知是沒有的算法,做什麼,你需要在Java的。我要做的是自己寫一個算法,這將是k-means算法的變體。本質上,您需要爲集羣類編寫一個名爲distanceFromCluster(Point p)的方法,該方法循環遍歷集羣中的每個點q並計算從p到q的距離。此方法將允許您確定點q是否足夠接近集羣,如果是,則應將其添加到該集合中... – phcoding 2013-04-08 12:59:46

+0

然後,此過程應包含在while循環中,並在所有點位於正確集羣中時終止 - 即在每個過程中沒有點移動到新的羣集時。 – phcoding 2013-04-08 13:00:32

1

要回答你的第一個問題,我創建了一個類來檢查天氣圖像包含一個非黑色填充的黑色輪廓圓。 這門課是實驗性的,它並不總是提供確切的結果,隨時編輯它並糾正你可能遇到的錯誤。 制定者不檢查空值或超出範圍值。

import java.awt.Point; 
import java.awt.image.BufferedImage; 
import java.io.File; 
import java.io.IOException; 
import java.util.ArrayList; 
import java.util.List; 

import javax.imageio.ImageIO; 

/** 
* Checks weather an image contains a single non black filled black outlined circle<br /> 
* This class is experimental, it does not provide exact results all the time, feel free to edit it and to correct 
* the bugs you might encounter. 
* @author  Ahmed KRAIEM 
* @version  0.9 alpha 
* @since  2013-04-03 
*/ 
public class CircleChecker { 

    private BufferedImage image; 

    /** 
    * Points that are equal to the calculated radius±<code>radiusesErrorMargin%</code> are not considered rogue points.<br /> 
    * <code>radiusesErrorMargin</code> must be <code>>0 && <1</code> 
    */ 
    private double radiusesErrorMargin = 0.2; 

    /** 
    * A shape that has fewer than roguePointSensitivity% of rogue points is considered a circle.<br /> 
    * <code>roguePointSensitivity</code> must be <code>>0 && <1</code> 
    */ 
    private double roguePointSensitivity = 0.05; 
    /** 
    * The presumed circle is divided into <code>angleCompartimentPrecision</code> parts,<br /> 
    * each part must have <code>minPointsPerCompartiment</code> points 
    * <code>angleCompartimentPrecision</code> must be <code>> 0</code> 
    */ 
    private int angleCompartimentPrecision = 50; 
    /** 
    * The minimum number of points requiered to declare a part valid.<br /> 
    * <code>minPointsPerCompartiment</code> must be <code>> 0</code> 
    */ 
    private int minPointsPerCompartiment = 20; 


    public CircleChecker(BufferedImage image) { 
     super(); 
     this.image = image; 
    } 

    public CircleChecker(BufferedImage image, double radiusesErrorMargin, 
      int minPointsPerCompartiment, double roguePointSensitivity, 
      int angleCompartimentPrecision) { 
     this(image); 
     this.radiusesErrorMargin = radiusesErrorMargin; 
     this.minPointsPerCompartiment = minPointsPerCompartiment; 
     this.roguePointSensitivity = roguePointSensitivity; 
     this.angleCompartimentPrecision = angleCompartimentPrecision; 
    } 

    public BufferedImage getImage() { 
     return image; 
    } 

    public void setImage(BufferedImage image) { 
     this.image = image; 
    } 

    public double getRadiusesErrorMargin() { 
     return radiusesErrorMargin; 
    } 

    public void setRadiusesErrorMargin(double radiusesErrorMargin) { 
     this.radiusesErrorMargin = radiusesErrorMargin; 
    } 

    public double getMinPointsPerCompartiment() { 
     return minPointsPerCompartiment; 
    } 

    public void setMinPointsPerCompartiment(int minPointsPerCompartiment) { 
     this.minPointsPerCompartiment = minPointsPerCompartiment; 
    } 

    public double getRoguePointSensitivity() { 
     return roguePointSensitivity; 
    } 

    public void setRoguePointSensitivity(double roguePointSensitivity) { 
     this.roguePointSensitivity = roguePointSensitivity; 
    } 

    public int getAngleCompartimentPrecision() { 
     return angleCompartimentPrecision; 
    } 

    public void setAngleCompartimentPrecision(int angleCompartimentPrecision) { 
     this.angleCompartimentPrecision = angleCompartimentPrecision; 
    } 

    /** 
    * 
    * @return true if the image contains no more than <code>roguePointSensitivity%</code> rogue points 
    * and all the parts contain at least <code>minPointsPerCompartiment</code> points. 
    */ 
    public boolean isCircle() { 
     List<Point> list = new ArrayList<>(); 
     final int xmin = image.getMinX(); 
     final int ymin = image.getMinY(); 

     final int ymax = ymin + image.getHeight(); 
     final int xmax = xmin + image.getWidth(); 

     for (int i = xmin; i < xmax; i++) { 
      for (int j = ymin; j < ymax; j++) { 

       int pixel = image.getRGB(i, j); 

       if ((pixel & 0x00FFFFFF) == 0) { 
        list.add(new Point(i, j)); 
       } 
      } 
     } 
     if (list.size() == 0) 
      return false; 
     double diameter = -1; 
     Point p1 = list.get(0); 
     Point across = null; 
     for (Point p2 : list) { 
      double d = distance(p1, p2); 
      if (d > diameter) { 
       diameter = d; 
       across = p2; 
      } 
     } 
     double radius = diameter/2; 
     Point center = center(p1, across); 
     int diffs = 0; 

     int diffsUntilError = (int) (list.size() * roguePointSensitivity); 
     double minRadius = radius - radius * radiusesErrorMargin; 
     double maxRadius = radius + radius * radiusesErrorMargin; 

     int[] compartiments = new int[angleCompartimentPrecision]; 


     for (int i=0; i<list.size(); i++) { 
      Point p = list.get(i); 
      double calRadius = distance(p, center); 
      if (calRadius>maxRadius || calRadius < minRadius) 
       diffs++; 
      else{ 
       //Angle 
       double angle = Math.atan2(p.y -center.y,p.x-center.x); 
       //angle is between -pi and pi 
       int index = (int) ((angle + Math.PI)/(Math.PI * 2/angleCompartimentPrecision)); 
       compartiments[index]++; 
      } 
      if (diffs >= diffsUntilError){ 
       return false; 
      } 
     } 
     int sumCompartiments = list.size() - diffs; 
     for(int comp : compartiments){ 
      if (comp < minPointsPerCompartiment){ 
       return false; 
      } 
     } 

     return true; 
    } 

    private double distance(Point p1, Point p2) { 
     return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)); 
    } 

    private Point center(Point p1, Point p2) { 
     return new Point((p1.x + p2.x)/2, (p1.y + p2.y)/2); 
    } 

    public static void main(String[] args) throws IOException { 
     BufferedImage image = ImageIO.read(new File("image.bmp")); 

     CircleChecker cc = new CircleChecker(image); 

     System.out.println(cc.isCircle()); 
    } 
}