53

I have this project where I need (on iOS) to detect simple geometric shapes inside an image.

enter image description here

After searching the internet I have concluded that the best tool for this is OpenCV. The thing is that up until two hours ago I had no idea what OpenCV is and I have never even remotely did anything involving image processing. My main experience is JS/HTML,C#,SQL,Objective-C...

Where do I start with this?

I have found this answer that I was able to digest and by reading already other stuff, I understand that OpenCV should return an Array of shapes with the points/corners, is this true? Also how will it represent a circle or a half circle? Also what about the shape orientation?

Do you know of any Demo iOS project that can demonstrate a similar functionality?

Community
  • 1
  • 1
Registered User
  • 3,669
  • 11
  • 41
  • 65

3 Answers3

91

If you have only these regular shapes, there is a simple procedure as follows :

  1. Find Contours in the image (image should be binary as given in your question)
  2. Approximate each contour using approxPolyDP function.
  3. Check number of elements in the approximated contours of all shapes to recognize shape. For eg, square will have 4, pentagon will have 5. Circles will have more, I don't know, so we find it. (I got 16 for circle and 9 for half-circle)
  4. Now assign the color, run the code for your test image, check its number, fill it with corresponding colors.

Below is my example in Python:

import numpy as np
import cv2

img = cv2.imread('shapes.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

ret,thresh = cv2.threshold(gray,127,255,1)

contours,h = cv2.findContours(thresh,1,2)

for cnt in contours:
    approx = cv2.approxPolyDP(cnt,0.01*cv2.arcLength(cnt,True),True)
    print len(approx)
    if len(approx)==5:
        print "pentagon"
        cv2.drawContours(img,[cnt],0,255,-1)
    elif len(approx)==3:
        print "triangle"
        cv2.drawContours(img,[cnt],0,(0,255,0),-1)
    elif len(approx)==4:
        print "square"
        cv2.drawContours(img,[cnt],0,(0,0,255),-1)
    elif len(approx) == 9:
        print "half-circle"
        cv2.drawContours(img,[cnt],0,(255,255,0),-1)
    elif len(approx) > 15:
        print "circle"
        cv2.drawContours(img,[cnt],0,(0,255,255),-1)

cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Below is the output:

enter image description here

Remember, it works only for regular shapes.

Alternatively to find circles, you can use houghcircles. You can find a tutorial here.

Regarding iOS, OpenCV devs are developing some iOS samples this summer, So visit their site: www.code.opencv.org and contact them.

You can find slides of their tutorial here

Aiq0
  • 306
  • 1
  • 11
Abid Rahman K
  • 51,886
  • 31
  • 146
  • 157
  • Thanks for the answer, just one more clarification. How do I detect the shape orientation? – Registered User Jul 11 '12 at 12:31
  • what do you mean by shape orientation. an example? – Abid Rahman K Jul 11 '12 at 16:09
  • For example the half circle: I want to know if the straight side is facing left, right, up or bottom – Registered User Jul 11 '12 at 16:23
  • Using the [moments](http://en.wikipedia.org/wiki/Image_moment). Orientations is "theta". Also, check [this](http://opencvpython.blogspot.gr/2012/06/contours-3-extraction.html). – LovaBill Apr 18 '13 at 07:19
  • 4
    This assumes that there is no other shapes. Detect usually means to find something among other things. So if there is a rectangle and square you won’t discriminate between them. Secondly, even a little noise will wrong the method of vertex counting. – Vlad Feb 23 '14 at 07:23
  • 4
    +1 - Vlad, all your points are correct. But here, please see the question, he is new to this, so he need something to get started, so my answer is very basic level. That is why i clearly mentioned in first sentence, 'if all shapes are regular'. OP didn't ask about square and rectangle, so i didn't touch that part. I like 'answer to the question' rather than providing all information he didn't ask. Let him find it himself and solve it. If he can't, let him come again and ask it specific. – Abid Rahman K Feb 23 '14 at 16:54
  • 2
    For this to work in Python 3, one variable needs to be added to the 6th line: img2,contours,h = cv2.findContours(thresh,1,2) – matt_jay Dec 03 '17 at 18:51
24

The answer depends on the presence of other shapes, level of noise if any and invariance you want to provide for (e.g. rotation, scaling, etc). These requirements will define not only the algorithm but also required pre-procesing stages to extract features.

Template matching that was suggested above works well when shapes aren't rotated or scaled and when there are no similar shapes around; in other words, it finds a best translation in the image where template is located:

double minVal, maxVal;
Point minLoc, maxLoc;
Mat image, template, result; // template is your shape
matchTemplate(image, template, result, CV_TM_CCOEFF_NORMED);
minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc); // maxLoc is answer

Geometric hashing is a good method to get invariance in terms of rotation and scaling; this method would require extraction of some contour points.

Generalized Hough transform can take care of invariance, noise and would have minimal pre-processing but it is a bit harder to implement than other methods. OpenCV has such transforms for lines and circles.

In the case when number of shapes is limited calculating moments or counting convex hull vertices may be the easiest solution: openCV structural analysis

Vlad
  • 4,425
  • 1
  • 30
  • 39
1

You can also use template matching to detect shapes inside an image.

chans
  • 497
  • 1
  • 5
  • 10
  • 3
    That's only if you know exactly how your shape will look like. If you have squares of different size, eg. you zoom in/out this method will not work that well I think. – Void Sep 12 '13 at 14:27
  • 2
    This link only answer is not appropriate. – Aerospace Sep 16 '14 at 07:37