2013-03-24 2 views
1

검은 색 원이있는 이미지가 있습니다.JUI를 사용하여 이미지에서 검정색 원 (픽셀 만이 아닌)을 감지합니다.

이미지는 OMR 설문지와 거의 같은 설문지 스캔 복사본입니다.

내가 사용 검게 된 원을 감지 할 JUI (필수 다른 API 경우)

내가 검색하는 동안 몇 가지 예를 가지고 있지만 나에게 정확한 결과를 제공 해달라고.

내가 시도 ... 은 우다이, 무는 ... 등 ...

그럼 난 내 자신을 만들기로 결정했다. 검정 픽셀을 감지 할 수 있지만 다음과 같습니다.

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] 또한 본인은 우다이 API는 그 돌봐 알고 .... 스캔 한 이미지가 기울어 져 있는지 알고 싶어하지만, 어떤 이유로 나는 실행 내 설문 조사 템플릿을 얻을 수 아니다 그 코드.

답변

0

원이 어떻게 생겼는지에 대한 템플릿을 프로그래밍 한 다음 다른 원 크기에 맞게 확장 할 수 있어야합니다.

예를 들어 반경 3의 원형은 다음과 같습니다이 당신이 어쩌면이 실현 될 5 × 5 또는 6 × 6까지 찾아야 원의 유한 집합을 가정

o 
ooo 
    o 

.

또는 다음을 사용할 수 있습니다. Midpoint circle algorithm
이렇게하면 모든 검정색 픽셀 그룹을 찾은 다음 각각의 중간 픽셀을 선택해야합니다.
외부 픽셀을 원으로 사용할 수있는 크기만큼의 guid로 사용하여이 알고리즘을 적용하십시오.
검정/예상 검정 픽셀의 차이 찾기.
예상되는 검은 색 비율이 충분히 높으면 검은 색 원이 나타나며이를 삭제하거나 희게 할 수 있습니다.

4

그래서 제대로 이해하면 검정 픽셀을 선택하는 코드가 생겨서 이제는 모든 검정 픽셀의 좌표를 가지며 원에 속한 모든 픽셀을 결정하려고합니다.

내가 접근 할 방법은 2 단계입니다.

1) 픽셀을 클러스터링합니다. 클러스터 목록을 포함하는 클러스터를 만들고 클러스터링 알고리즘을 사용하여 모든 지점을 올바른 클러스터에 배치하십시오.

2) 클러스터가 원인지 확인하십시오. 이렇게하려면 각 클러스터의 모든 점의 중간 점을 찾으십시오 (모든 점의 평균을 취하십시오). 그런 다음 가운데에서 최소 및 최대 거리를 찾으십시오. 이들의 차이는 파일에있는 원의 최대 두께보다 작아야합니다. 이것들은 원 안에 들어있는 가장 안쪽과 가장 바깥 쪽 원에 대한 반지름을 줄 것입니다.이제 x = 2 + y^2 = radius 방정식을 사용하여 클러스터가 포함해야하는 점을 찾기 위해 이전에 찾은 최대 값과 최소값 사이의 값으로 반경을 설정하십시오. 클러스터에 이들이 포함되어 있으면 원입니다.

물론 고려해야 할 다른 고려 사항은 원형이 아닌 대략적인 타원이 있는지 여부입니다.이 경우 타원 방정식을 사용해야합니다. 또한 파일에 원과 같은 모양이 포함되어있는 경우이를 제외시키기 위해 추가 코드를 작성해야합니다. 반대로 모든 서클이 정확히 같은 크기라면 알고리즘에서 해당 크기의 서클 만 검색하도록해야하므로 작업을 줄일 수 있습니다.

내가 도움이 될 수 있기를 바랍니다. 행운을 빈다.

+0

나는이 방법이 나를 위해 일을해야한다고 생각 ...하지만 당신은 내게이 도움 것이 자바의 클러스터링 알고리즘을 제안 할 수 있습니다 .. –

+0

내가 아는 어떤 알고리즘은 당신이 필요 않는다는 없다 자바. 내가 뭘 할 것인가는 k- 평균 알고리즘의 변형 일 것이다. 본질적으로 클러스터의 각 포인트 q를 반복하고 p에서 q까지의 거리를 계산하는 distanceFromCluster (Point p)라는 클러스터 클래스에 대한 메소드를 작성해야합니다. 이 방법을 사용하면 점 q가 클러스터에 충분히 가까운 지 여부를 결정할 수 있습니다. 따라서이 점을 추가해야합니다 ... – phcoding

+0

이 절차는 while 루프에 포함되어야하며 모든 점이 올바른 클러스터에있을 때 종료되어야합니다 즉, 각 패스에서 더 이상 새로운 클러스터로 포인트가 이동하지 않을 때. – phcoding

1

첫 번째 질문에 답하기 위해 이미지에 날씨가 아닌 검은 색으로 채워진 검은 색 윤곽선이 하나만 들어있는 클래스를 만들었습니다. 이 클래스는 실험적이며 항상 정확한 결과를 제공하지는 않으며 편집 할 수 있으며 발생할 수있는 버그를 수정할 수 있습니다. setter는 null 또는 범위를 벗어난 값을 확인하지 않습니다.

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()); 
    } 
} 
관련 문제