-2

I have a problem with calculating thinness ratio. It gives me value of 0.9 instead of 1.

Thinness ratio is given as a

(4 * pi * area) / (perimeter * perimeter)

For circle, the above equation, should give a result 1.0.

image of circle

import cv2
import math
image = cv2.imread("circle.png")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cnt = cv2.findContours(gray.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1]

for c in cnt:
            # compute the area of the contour along with the bounding box
            # to compute the aspect ratio
            area = cv2.contourArea(c)
            perimeter = cv2.arcLength(c,True)
ti= (4*area*math.pi)/(perimeter**2)
print(ti)
Sam Mason
  • 15,216
  • 1
  • 41
  • 60
stacker
  • 3
  • 3
  • 1
    Welcome to StackOverflow. Please read and follow the posting guidelines in the help documentation, as suggested when you created this account. [Minimal, complete, verifiable example](https://stackoverflow.com/help/minimal-reproducible-example) applies here. We cannot effectively help you until you post your MCVE code and accurately specify the problem. We should be able to paste your posted code into a text file and reproduce the problem you specified. Your posted code includes graphic input, two function calls, and a calculation. Where in all of that has the discrepancy occurred? – Prune Sep 19 '19 at 18:00
  • Did you try `cv2.CHAIN_APPROX_NONE`? – Mark Setchell Sep 19 '19 at 19:00
  • The `cv2.CHAIN_APPROX_NONE` function gives me the same results – stacker Sep 20 '19 at 08:41

1 Answers1

0

I think this is due to the contour finding code returning an exact outline which, maybe due to aliasing errors, is actually longer than it should be. If I smooth the resulting contour I get the "right" value of ~1.

import cv2
import numpy as np
from scipy.interpolate import splprep, splev

image = cv2.imread("circle.png", 0)
_, thresh = cv2.threshold(image, 127, 255, 0)

# remove extra dimensions here to simplify later code
[contour], hierarchy = cv2.findContours(thresh, 1, 2)
contour = contour[:,0,:]

# smooth contour
tck, u = splprep(contour.T, u=None, s=30, per=1)
u_new = np.linspace(u.min(), u.max(), 50)
new_cont = np.column_stack(splev(u_new, tck, der=0))

# change types to stop cv2 complaining
new_cont = new_cont.astype(np.float32)

# calculate as before
area = cv2.contourArea(new_cont)
circum = cv2.arcLength(new_cont, True)

(4 * pi * area) / (circum**2)

giving me 0.9986, which is OK for me. Note that if you're running OpenCV version 3 you'll need to change the findContours line as it returns a triple instead of a pair of values.

I've borrowed the bspline smoothing code from here which seems to have in turn been borrowed from here. You can play with the various smoothing factors (try taking s down to 1), and where to sample it (change the last parameter to linspace).

Sam Mason
  • 15,216
  • 1
  • 41
  • 60
  • Thank you Sam! I was also aware about aliasing errors, but I didn't think it was the reason. The line 9 should be changed to `_, [contour], hierarchy = cv2.findContours(thresh, 1, 2)` as stated here [link](https://stackoverflow.com/questions/25504964/opencv-python-valueerror-too-many-values-to-unpack). I need to test the code with my other cases today, and if it works I will mark your answer as accepted. – stacker Sep 20 '19 at 09:00
  • huh, you must be running quite an old version, it apparently changed [a year ago](https://github.com/opencv/opencv/commit/8eb987e39351fa1e28c1d2919c86cc6483b6b414). might be worth updating if you can, there will presumably be lots of other bug fixes as well – Sam Mason Sep 20 '19 at 09:19
  • You are right, thanks again! I checked and I use the one, which comes with anaconda package. It is pretty old then. – stacker Sep 20 '19 at 09:40