Improved maximum inscribed circle algorithm to determine crack contour width

Some time ago, I organized the code of the maximum inscribed circle algorithm on the Internet. The code uploaded by the original blogger was a bit messy, maybe it was used by itself. You can read this compiled code: Maximum inscribed circle algorithm to calculate crack width .

Detailed explanation of the maximum inscribed circle algorithm

A circle that is tangent to every edge or curve of a given polygon or curve. What we need to calculate is the largest inscribed circle of the contour of a given image, that is, the circle with the largest diameter among the circles that are tangent to each edge of the contour. So the diameter is the width of our outline.

Since the inscribed circle of the outline is required, from the characteristics of the circle, if you want to uniquely determine a circle, you need to know its center and radius. Okay, now the problem is cleverly transformed from finding the inscribed circle of the contour to finding the distance and relationship between a certain point and a polygon.

There is a function pointPolygonTest in opencv that can get the relationship between a certain point and a polygon. For example, whether the point is inside, outside, or on the polygon, you can also get the pixel distance between the point and the polygon. That problem is actually very easy to solve. We then use cv2.minMaxLoc(src) to obtain the position of the minimum and maximum values ​​​​in the given array. Its syntax is as follows

min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(src)

where src is the input array or image. The function returns the minimum value min_val and the maximum value max_val, as well as their positions in the array, the position of the minimum value min_loc and the position of the maximum value max_loc. 

At this point, I think everyone knows how to find the width of the outline, which is: max_loc * 2.

Detailed code

import cv2
import string
import numpy as np
import pyzjr as pz

def incircle(img, contours_arr, color=(0, 0, 255)):
    """
    轮廓最大内切圆算法,所有轮廓当中的内切圆
    """
    result = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
    raw_dist = np.zeros(img.shape, dtype=np.float32)
    letters = list(string.ascii_uppercase)
    label = {}
    for k, contours in enumerate(contours_arr):
        for i in range(img.shape[0]):
            for j in range(img.shape[1]):
                raw_dist[i, j] = cv2.pointPolygonTest(contours, (j, i), True)
        min_val, max_val, _, max_dist_pt = cv2.minMaxLoc(raw_dist)
        label[letters[k]] = max_val * 2
        radius = int(max_val)
        cv2.circle(result, max_dist_pt, radius, color, 1, 1, 0)

    return result, label


if __name__=="__main__":
    path = r"D:\PythonProject\RoadCrack\dimension2_data\num/001.png"
    img = cv2.imread(path)
    gray_img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    thresh = pz.BinaryImg(img)
    contours_arr, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)

    result, label = incircle(gray_img, contours_arr)
    print("裂缝宽度:",label)
    cv2.imwrite("result.png",result)

To detect contours first, you can use opencv's findContours. Need to ensure that it is in the form of ndarray array.

Crack width: {'A': 5.656854152679443, 'B': 4.4721360206604}

Now we can know the maximum inscribed circle diameter corresponding to the two cracks, that is, the width of the crack.

Algorithm comparison

And from a time perspective:

  • Original maximum inscribed circle algorithm: 1.79125 sec
  • Improved inscribed circle algorithm: 1.05487 sec

From the calculated diameter:

  • Original maximum inscribed circle algorithm: 13.81
  • Improved inscribed circle algorithm: {'A': 14.0}

The implementation here is relatively simple, except that there are more nested loops, but it can store the width corresponding to each crack. 

Guess you like

Origin blog.csdn.net/m0_62919535/article/details/133387359