Reputation: 877
I have these two mask images:
Image1
Image2
The first image I have the real width. And I would like to measure the width of several points, like this:
I made this code based on this (https://www.pyimagesearch.com/2016/03/28/measuring-size-of-objects-in-an-image-with-opencv/), and I got just the total width:
Follow my adapted code:
from scipy.spatial import distance as dist
from imutils import perspective
from imutils import contours
import numpy as np
import argparse
import imutils
import cv2
def midpoint(ptA, ptB):
return ((ptA[0] + ptB[0]) * 0.5, (ptA[1] + ptB[1]) * 0.5)
# Read image
mask2 = cv2.imread('Image1.jpg')
mask4 = cv2.imread('Image2.jpg')
cv2.imshow("Image", mask2)
cv2.waitKey(0)
########### Mask 2 - Oject
gray = cv2.cvtColor(mask2, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (7, 7), 0)
# perform edge detection, then perform a dilation + erosion to
# close gaps in between object edges
edged = cv2.Canny(gray, 50, 100)
edged = cv2.dilate(edged, None, iterations=1)
edged = cv2.erode(edged, None, iterations=1)
# find contours in the edge map
cnts_1 = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts_1 = imutils.grab_contours(cnts_1)
# sort the contours from left-to-right and initialize the
# 'pixels per metric' calibration variable
(cnts_1, _) = contours.sort_contours(cnts_1)
pixelsPerMetric = None
contorno=cv2.drawContours(mask2,cnts_1, -1, (0, 0, 255), 1)
cv2.imshow("Chaveiro", contorno)
cv2.waitKey()
##### Mask 4 - EGS
gray = cv2.cvtColor(mask4, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (7, 7), 0)
# perform edge detection, then perform a dilation + erosion to
# close gaps in between object edges
edged = cv2.Canny(gray, 50, 100)
edged = cv2.dilate(edged, None, iterations=1)
edged = cv2.erode(edged, None, iterations=1)
# find contours in the edge map
cnts_2 = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts_2 = imutils.grab_contours(cnts_2)
# sort the contours from left-to-right and initialize the
# 'pixels per metric' calibration variable
(cnts_2, _) = contours.sort_contours(cnts_2)
pixelsPerMetric = None
contorno2=cv2.drawContours(mask4,cnts_2, -1, (0, 255, 0), 1)
cv2.imshow("EGS", mask4)
cv2.waitKey()
###### Total Contourn
cnts=(cnts_1 + cnts_2)
# loop over the contours individually
for c in cnts:
# if the contour is not sufficiently large, ignore it
if cv2.contourArea(c) < 100:
continue
# compute the rotated bounding box of the contour
orig = (mask2 + mask4)
box = cv2.minAreaRect(c)
box = cv2.cv.BoxPoints(box) if imutils.is_cv2() else cv2.boxPoints(box)
box = np.array(box, dtype="int")
# order the points in the contour such that they appear
# in top-left, top-right, bottom-right, and bottom-left
# order, then draw the outline of the rotated bounding
# box
box = perspective.order_points(box)
cv2.drawContours(orig, [box.astype("int")], -1, (0, 255, 0), 2)
# loop over the original points and draw them
for (x, y) in box:
cv2.circle(orig, (int(x), int(y)), 5, (0, 0, 255), -1)
# unpack the ordered bounding box, then compute the midpoint
# between the top-left and top-right coordinates, followed by
# the midpoint between bottom-left and bottom-right coordinates
(tl, tr, br, bl) = box
(tltrX, tltrY) = midpoint(tl, tr)
(blbrX, blbrY) = midpoint(bl, br)
# compute the midpoint between the top-left and top-right points,
# followed by the midpoint between the top-righ and bottom-right
(tlblX, tlblY) = midpoint(tl, bl)
(trbrX, trbrY) = midpoint(tr, br)
# draw the midpoints on the image
cv2.circle(orig, (int(tltrX), int(tltrY)), 5, (255, 0, 0), -1)
cv2.circle(orig, (int(blbrX), int(blbrY)), 5, (255, 0, 0), -1)
cv2.circle(orig, (int(tlblX), int(tlblY)), 5, (255, 0, 0), -1)
cv2.circle(orig, (int(trbrX), int(trbrY)), 5, (255, 0, 0), -1)
# draw lines between the midpoints
cv2.line(orig, (int(tltrX), int(tltrY)), (int(blbrX), int(blbrY)),
(255, 0, 255), 2)
cv2.line(orig, (int(tlblX), int(tlblY)), (int(trbrX), int(trbrY)),
(255, 0, 255), 2)
# compute the Euclidean distance between the midpoints
dA = dist.euclidean((tltrX, tltrY), (blbrX, blbrY))
dB = dist.euclidean((tlblX, tlblY), (trbrX, trbrY))
# if the pixels per metric has not been initialized, then
# compute it as the ratio of pixels to supplied metric
# (in this case, mm)
if pixelsPerMetric is None:
pixelsPerMetric = dB / 5.6
# compute the size of the object
dimA = dA / pixelsPerMetric
print(dimA)
dimB = dB / pixelsPerMetric
print(dimB)
# draw the object sizes on the image
cv2.putText(orig, "{:.1f}mm".format(dimA),
(int(tltrX - 15), int(tltrY - 10)), cv2.FONT_HERSHEY_SIMPLEX,
0.65, (0, 255, 255), 2)
cv2.putText(orig, "{:.1f}mm".format(dimB),
(int(trbrX + 10), int(trbrY)), cv2.FONT_HERSHEY_SIMPLEX,
0.65, (0, 255, 255), 2)
# show the output image
cv2.imshow("Image", orig)
cv2.waitKey(0)
Some sugestion?
Upvotes: 0
Views: 2358
Reputation: 53081
Here is one way to do that in Python/OpenCV. Basically, get the angle of the rotated rectangle and unrotate the image. Then crop it. Then count the number of non-zero pixels for each row in the cropped image.
Input:
import cv2
import numpy as np
img = cv2.imread("blob2.png")
hh, ww = img.shape[:2]
# convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# create a binary image
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
# get the single external contours
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)
# get rotated rectangle from contour
# get its dimensions
# get angle relative to horizontal from rotated rectangle
rotrect = cv2.minAreaRect(big_contour)
(center), (width,height), angle = rotrect
box = cv2.boxPoints(rotrect)
boxpts = np.int0(box)
# draw rotated rectangle on copy of input
rotrect_img = img.copy()
cv2.drawContours(rotrect_img,[boxpts],0,(0,0,255),1)
# from https://www.pyimagesearch.com/2017/02/20/text-skew-correction-opencv-python/
# the `cv2.minAreaRect` function returns values in the
# range [-90, 0); as the rectangle rotates clockwise the
# returned angle tends to 0 -- in this special case we
# need to add 90 degrees to the angle
if angle < -45:
angle = -(90 + angle)
# otherwise, check width vs height
else:
if width > height:
angle = -(90 + angle)
else:
angle = -angle
# negate the angle to unrotate
neg_angle = -angle
print('unrotation angle:', neg_angle)
print('')
# Get rotation matrix
M = cv2.getRotationMatrix2D(center, neg_angle, scale=1.0)
# unrotate to rectify
rectified = cv2.warpAffine(gray, M, (ww, hh), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)
# threshold it again to binary
rectified = cv2.threshold(rectified, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
# get bounding box of contour of rectified image
cntrs = cv2.findContours(rectified, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cntrs = cntrs[0] if len(cntrs) == 2 else cntrs[1]
cntr = cntrs[0]
x,y,w,h = cv2.boundingRect(cntr)
# crop to blob limits
crop = rectified[y:y+h, x:x+w]
# get width at every row of crop
count = np.count_nonzero(crop, axis=1)
print('row : count')
for i in range(h):
print(i, ":", count[i])
# save images
cv2.imwrite("blob2_rotrect.png",rotrect_img)
cv2.imwrite("blob2_rectified.png",rectified)
cv2.imwrite("blob2_rectified_cropped.png",crop)
# display result, though it won't show transparency
cv2.imshow("thresh", thresh)
cv2.imshow("rot_rect", rotrect_img)
cv2.imshow("rectified", rectified)
cv2.imshow("crop", crop)
cv2.waitKey(0)
cv2.destroyAllWindows()
Rotated Rectangle Image:
Rectified (unrotated) Image:
Rectified and Cropped Image:
Count of Non-Zero Pixels per row of cropped image:
row : count
0 : 2
1 : 3
2 : 4
3 : 5
4 : 7
5 : 7
6 : 9
7 : 10
8 : 11
9 : 13
10 : 14
11 : 15
12 : 16
13 : 18
14 : 18
15 : 20
16 : 21
17 : 22
18 : 22
19 : 22
20 : 22
21 : 22
22 : 22
23 : 22
24 : 22
25 : 22
26 : 21
27 : 22
28 : 21
29 : 21
30 : 21
31 : 21
32 : 21
33 : 22
34 : 20
35 : 20
36 : 19
37 : 19
38 : 19
39 : 19
40 : 18
41 : 19
42 : 17
43 : 17
44 : 18
45 : 17
46 : 17
47 : 16
48 : 17
49 : 17
50 : 17
51 : 16
52 : 17
53 : 17
54 : 17
55 : 16
56 : 16
57 : 18
58 : 17
59 : 16
60 : 17
61 : 17
62 : 17
63 : 16
64 : 16
65 : 18
66 : 17
67 : 17
68 : 17
69 : 17
70 : 18
71 : 17
72 : 17
73 : 17
74 : 18
75 : 18
76 : 18
77 : 19
78 : 19
79 : 18
80 : 19
81 : 19
82 : 19
83 : 19
84 : 19
85 : 20
86 : 19
87 : 18
88 : 20
89 : 20
90 : 20
91 : 20
92 : 20
93 : 20
94 : 19
95 : 20
96 : 20
97 : 20
98 : 20
99 : 20
100 : 20
101 : 20
102 : 21
103 : 20
104 : 21
105 : 20
106 : 21
107 : 21
108 : 20
109 : 22
110 : 21
111 : 21
112 : 21
113 : 22
114 : 23
115 : 24
116 : 24
117 : 24
118 : 24
119 : 24
120 : 24
121 : 26
122 : 27
123 : 26
124 : 27
125 : 28
126 : 28
127 : 30
128 : 30
129 : 30
130 : 32
131 : 33
132 : 32
133 : 34
134 : 35
135 : 35
136 : 36
137 : 36
138 : 38
139 : 38
140 : 39
141 : 40
142 : 40
143 : 41
144 : 42
145 : 43
146 : 44
147 : 44
148 : 45
149 : 45
150 : 46
151 : 46
152 : 48
153 : 49
154 : 50
155 : 51
156 : 52
157 : 52
158 : 53
159 : 54
160 : 54
161 : 55
162 : 56
163 : 57
164 : 57
165 : 58
166 : 58
167 : 59
168 : 60
169 : 60
170 : 62
171 : 63
172 : 62
173 : 63
174 : 63
175 : 64
176 : 66
177 : 64
178 : 65
179 : 66
180 : 65
181 : 58
182 : 54
183 : 52
184 : 51
185 : 50
186 : 46
187 : 46
188 : 45
189 : 44
190 : 45
191 : 44
192 : 44
193 : 44
194 : 44
195 : 44
196 : 44
197 : 44
198 : 43
199 : 43
200 : 43
201 : 43
202 : 43
203 : 43
204 : 41
205 : 42
206 : 42
207 : 41
208 : 42
209 : 41
210 : 41
211 : 41
212 : 40
213 : 41
214 : 40
215 : 41
216 : 41
217 : 40
218 : 41
219 : 39
220 : 41
221 : 41
222 : 41
223 : 42
224 : 41
225 : 42
226 : 43
227 : 43
228 : 44
229 : 45
230 : 45
231 : 46
232 : 47
233 : 47
234 : 48
235 : 47
236 : 49
237 : 50
238 : 50
239 : 50
240 : 51
241 : 50
242 : 51
243 : 51
244 : 52
245 : 53
246 : 53
247 : 54
248 : 54
249 : 54
250 : 55
251 : 54
252 : 55
253 : 56
254 : 56
255 : 56
256 : 57
257 : 57
258 : 57
259 : 57
260 : 57
261 : 58
262 : 57
263 : 57
264 : 57
265 : 57
266 : 57
267 : 56
268 : 53
269 : 54
270 : 50
271 : 41
272 : 35
273 : 34
274 : 31
275 : 31
276 : 30
277 : 29
278 : 27
279 : 27
280 : 27
281 : 24
282 : 24
283 : 23
284 : 21
285 : 21
286 : 20
287 : 19
288 : 18
289 : 17
290 : 16
291 : 15
292 : 15
293 : 14
294 : 13
295 : 12
296 : 10
297 : 9
298 : 8
299 : 5
300 : 2
Upvotes: 3