基于另一张蒙版图像测量蒙版图像中几个点的宽度

Measuring the width of several points in a mask image based on another mask image

我有这两张蒙版图片:

图片1

图片2

第一张图片我有实际宽度。我想测量几个点的宽度,像这样:

我根据这个(https://www.pyimagesearch.com/2016/03/28/measuring-size-of-objects-in-an-image-with-opencv/)做了这个代码,我得到的只是总宽度:

按照我改编的代码:

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)

有什么建议吗?

这是 Python/OpenCV 中的一种方法。基本上,获取旋转矩形的角度并取消旋转图像。然后裁剪它。然后计算裁剪图像中每行的非零像素数。

输入:

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()

旋转矩形图像:

校正(未旋转)图像:

校正和裁剪图像:

每行裁剪图像的非零像素数:

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