Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”
This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.
Python OpenCV 365 day learning plan, enter the graphics realm with eraser. This blog is the 37th in the series.
Foundation of basic knowledge
This article needs to be watched together with the previous one, of course, in order to better learning effect, let’s review it again.
The previous blog focused on the use of two functions. The first is the findContours function, which is used to detect contours. The prototype of this function is as follows:
findContours(image, mode, method[, contours[, hierarchy[, offset]]]) -> contours, hierarchy
Copy the code
Write a simple code to test it:
import cv2 as cv
import numpy as np
img = cv.imread("./test1.jpg".0)
img = cv.medianBlur(img, 5)
ret, thresh = cv.threshold(img, 0.255, cv.THRESH_BINARY | cv.THRESH_OTSU)
contours, hierarchy = cv.findContours(
thresh, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE)
print(contours, type(contours), len(contours))
Copy the code
The following output is displayed. Note the following:
[array([[[0, 0]],
[[0, 1]],
[[0, 2]],
...,
[[3, 0]],
[[2, 0]],
[[1, 0]]], dtype=int32)] <class 'list'> 1
Copy the code
The CV. FindContours edge detection function returns only a list of items, each of which is an array in NUMpy, representing boundary values. If I have one list, I’m only going to get one boundary. Next, draw the edges, where there is a bit more to adjust, the code is as follows.
import cv2 as cv
import numpy as np
img = cv.imread("./ddd.jpg")
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
med_img = cv.medianBlur(gray, 7)
ret, thresh = cv.threshold(med_img, 225.255, cv.THRESH_BINARY_INV)
# cv.imshow("thresh", thresh)
contours, hierarchy = cv.findContours(
thresh, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE)
print(contours, type(contours), len(contours), contours[0].shape)
print(hierarchy, type(hierarchy), len(hierarchy), hierarchy.shape)
dst = cv.drawContours(img, contours, -1, (0.0.255), 2)
cv.imshow("dst", dst)
cv.waitKey()
Copy the code
The output involved is as follows:
print(contours, type(contours), len(contours), contours[0].shape)
print(hierarchy, type(hierarchy), len(hierarchy), hierarchy.shape)
Copy the code
The number given by len(contours) is the number of boundaries.
Below draw outline code, you can have a try.
dst = cv.drawContours(img, contours, 0, (0.0.255), 2)
Copy the code
ContourIdx is the index of found contours. It cannot exceed the total number of contours, otherwise the following bugs will occur.
error: (-215:Assertion failed) 0 <= contourIdx && contourIdx < (int)last in function 'cv::drawContours'
Copy the code
Go ahead and change the last parameter to -1, resulting in the following result.
Change threshold segmentation to edge detection
Above, we achieved the final effect through the cv2.threshold function. Next, we carried out the above operation through the Canny edge detection.
import cv2 as cv
import numpy as np
img = cv.imread("./ddd.jpg")
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
med_img = cv.medianBlur(gray, 5)
# threshold segmentation
# ret, thresh = cv.threshold(med_img, 225, 255, cv.THRESH_BINARY_INV)
# cv.imshow("thresh", thresh)
edges = cv.Canny(med_img,200.255)
cv.imshow("edges",edges)
contours, hierarchy = cv.findContours(
edges, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE)
# print(contours, type(contours), len(contours), contours[0].shape)
# print(hierarchy, type(hierarchy), len(hierarchy), hierarchy.shape)
dst = cv.drawContours(img, contours, -1, (0.0.255), -1)
cv.imshow("dst", dst)
cv.waitKey()
Copy the code
With the above code, it is difficult to achieve a fill effect because many paths are not closed. Parameter adjustment is not very ideal, forgive me.
In the adjustment of parameters, the following situation also occurred, which did not find a reasonable explanation.
Object to measure
After the contour is obtained, some geometric characteristics of the contour can be measured, including the distance from the origin, the distance from the center, and the coordinates of the center of gravity of the image. These mathematical concepts are left to the subsequent learning, and the application layer can be mastered first.
The function used for object measurement is Cv2.moments, which is modeled as follows:
retval = cv2.moments(array[, binaryImage])
Copy the code
Use is input contour, return a dictionary, test code as follows:
# dst = cv.drawContours(img, contours, -1, (200, 100, 0), 3)
for contour in contours:
print(cv.moments(contour))
Copy the code
I chose one as an illustration, which reads as follows:
{'m00': 3.0.'m10': 213.0.'m01': 295.5.'m20': 15125.5.'m11': 20982.75.'m02': 29109.0.'m30': 1074265.5.'m21': 1490181.25.'m12': 2067182.25.'m03': 2867679.75.'mu20': 2.5.'mu11': 2.25.'mu02': 2.25.'mu30': 0.0.'mu21': 0.0.'mu12': 0.0.'mu03': 0.0.'nu20': 0.2777777777777778.'nu11': 0.25.'nu02': 0.25.'nu30': 0.0.'nu21': 0.0.'nu12': 0.0.'nu03': 0.0}
Copy the code
Then you can do the corresponding processing for the above content. For example, find the center of mass of the contour.
The most common error is to add a non-zero branch validation.
ZeroDivisionError: float division by zero
Copy the code
The second error is a type error, which we learned about when we learned to draw circles, as follows.
TypeError: integer argument expected, got float
Copy the code
The logic after modifying the code is as follows:
import cv2 as cv
import numpy as np
img = cv.imread("./t1.jpg")
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
med_img = cv.medianBlur(gray, 7)
# threshold segmentation
# ret, thresh = cv.threshold(med_img, 150, 255, cv.THRESH_BINARY_INV)
# cv.imshow("thresh", thresh)
edges = cv.Canny(med_img, 200.255)
cv.imshow("edges", edges)
contours, hierarchy = cv.findContours(
edges, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE)
# print(contours, type(contours), len(contours), contours[0].shape)
# print(hierarchy, type(hierarchy), len(hierarchy), hierarchy.shape)
# dst = cv.drawContours(img, contours, -1, (200, 100, 0), 3)
for contour in contours:
m = cv.moments(contour)
if m['m00'] != 0:
x = int(m['m10']/m['m00'])
y = int(m['m01']/m['m00'])
cv.circle(img, (x, y), 2, (0.0.255), -1)
else:
pass
dst = cv.drawContours(img, contours, -1, (200.100.0), 2)
cv.imshow("img", img)
cv.waitKey()
Copy the code
You can also calculate the contourArea using the function cv2.contourArea.
for contour in contours:
print(cv.contourArea(contour))
Copy the code
The area obtained when the curve is closed is the area surrounded by the contour. If the contour is not closed or the boundary is crossed, the obtained area is not accurate.