如何检测灰度图像上几乎不可见的线条并计算其长度

问题描述 投票:0回答:1

我正在尝试用Python编写基于OpenCV库的计算机视觉代码来检测强度接近背景的水平线。请参阅下图示例。

我尝试了两种方法。第一个基于 Canny 边缘检测和 Hough 变换,但它只检测到几行(参见下面的代码和图像)。

import math
import numpy as np
import cv2

scaleFactor = 1

maskX1 = 57
maskX2 = 263
maskY1 = 30
maskY2 = 164

angleStart = -1
angleEnd = 1

verticalKernel = np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]])
sharpenKernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]])

def applyKernel(image, kernel):
    return cv2.filter2D(image, -1, kernel)


# read image
image_c = cv2.imread('images/1.png')

image_c = cv2.resize(image_c, None, fx=scaleFactor, fy=scaleFactor, interpolation=cv2.INTER_CUBIC)

cv2.imshow('Original Image', image_c)

# convert to grayscale
image_g = cv2.cvtColor(image_c, cv2.COLOR_RGB2GRAY)
# image_g = cv2.bilateralFilter(image_g, 15, 15, 15)

image_g = applyKernel(image_g, sharpenKernel)
cv2.imshow('Sharpen Image', image_g)

image_g = applyKernel(image_g, verticalKernel)

cv2.imshow('Vertical Sobel Operator', image_g)

# Gaussian blur and Canny
threshold_low = 250
threshold_high = 300

image_canny = cv2.Canny(image_g, threshold_low, threshold_high)
cv2.imshow('Canny Image', image_canny)

# Visualize region of interest
mask = np.zeros_like(image_g)
vertices = np.array([[(maskX1 * scaleFactor, maskY1 * scaleFactor), (maskX2 * scaleFactor, maskY1 * scaleFactor), (maskX2 * scaleFactor, maskY2 * scaleFactor), (maskX1 * scaleFactor, maskY2 * scaleFactor)]], dtype=np.int32)
cv2.fillPoly(mask, vertices, 255)
masked_image = cv2.bitwise_and(image_canny, mask)

# masked_image = image_canny
cv2.imshow('Region of interest', masked_image)

rho = 1 * scaleFactor  # distance resolution in pixels
theta = np.pi / 180  # angular resolution in radians
threshold = 3  # minimum number of votes
min_line_len = 10 * scaleFactor  # minimum number of pixels making up a line
max_line_gap = 20 * scaleFactor  # maximum gap in pixels between connectable line segments

lines = cv2.HoughLinesP(masked_image, rho, theta, threshold, np.array([]), minLineLength=min_line_len,
                        maxLineGap=max_line_gap)

line_image = np.zeros((masked_image.shape[0], masked_image.shape[1], 3), dtype=np.uint8)

numLines = 0
totalLineLength = 0
for line in lines:
    for x1, y1, x2, y2 in line:
        if x2 == x1:
            lineAngle = 90
        else:
            lineAngle = math.degrees(math.atan((y2 - y1) / (x2 - x1)))
        if angleStart < lineAngle < angleEnd:
            cv2.line(line_image, (x1, y1), (x2, y2), [0, 0, 255], 2)
            numLines = numLines + 1
            totalLineLength = totalLineLength + math.sqrt((x2 - x1)**2 + (y2 - y1)**2)

α = 1
β = 0.3
γ = 0

# Resultant weighted image is calculated as follows: original_img * α + img * β + γ
image_with_lines = cv2.addWeighted(image_c, α, line_image, β, γ)
cv2.imshow('Image with lines', image_with_lines)

cv2.waitKey()
cv2.destroyAllWindows()

第二种方法基于图像阈值和轮廓分析,但结果也令人失望(参见下面的代码和图像)。

import math
import numpy as np
import cv2

scaleFactor = 1

maskX1 = 57
maskX2 = 263
maskY1 = 30
maskY2 = 164

angleStart = -5
angleEnd = 5

verticalKernel = np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]])
sharpenKernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]])

basePath = 'images/'
fileExtension = '.png'

def applyKernel(image, kernel):
    return cv2.filter2D(image, -1, kernel)

def getHoughLines(image, masked_image):
    rho = 1 * scaleFactor  # distance resolution in pixels
    theta = np.pi / 180  # angular resolution in radians
    threshold = 3  # minimum number of votes
    min_line_len = 10 * scaleFactor  # minimum number of pixels making up a line
    max_line_gap = 5 * scaleFactor  # maximum gap in pixels between connectable line segments

    lines = cv2.HoughLinesP(masked_image, rho, theta, threshold, np.array([]), minLineLength=min_line_len,
                            maxLineGap=max_line_gap)

    line_image = np.zeros((masked_image.shape[0], masked_image.shape[1]), dtype=np.uint8)

    numLines = 0
    totalLineLength = 0
    for line in lines:
        for x1, y1, x2, y2 in line:
            if x2 == x1:
                lineAngle = 90
            else:
                lineAngle = math.degrees(math.atan((y2 - y1) / (x2 - x1)))
            if angleStart < lineAngle < angleEnd:
                cv2.line(line_image, (x1, y1), (x2, y2), 255, 2)
                numLines = numLines + 1
                totalLineLength = totalLineLength + math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)

    α = 1
    β = 0.3
    γ = 0

    # Resultant weighted image is calculated as follows: original_img * α + img * β + γ

    image_with_lines = cv2.addWeighted(image, α, line_image, β, γ)
    cv2.imshow('Image with lines', image_with_lines)

    return image_with_lines

# read image
image_g = cv2.imread('images/1.png', cv2.IMREAD_GRAYSCALE)

# image_g = cv2.resize(image_g, None, fx=scaleFactor, fy=scaleFactor, interpolation=cv2.INTER_CUBIC)

cv2.imshow('Original Image', image_g)

# Apply Gaussian blur to reduce noise
# image_blurred = cv2.GaussianBlur(image_g, (5, 5), 0)
image_blurred = image_g

image_blurred = applyKernel(image_blurred, sharpenKernel)
cv2.imshow('Sharpen Image', image_blurred)

image_blurred = applyKernel(image_blurred, verticalKernel)

cv2.imshow('Vertical Sobel Operator', image_blurred)

# Apply adaptive thresholding to binarize the image
# _, binary_image = cv2.threshold(image_blurred, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
_, binary_image = cv2.threshold(image_blurred, 70, 255, cv2.THRESH_BINARY)

cv2.imshow('Binary image', binary_image)

# Visualize region of interest
mask = np.zeros_like(image_g)
vertices = np.array([[(maskX1 * scaleFactor, maskY1 * scaleFactor), (maskX2 * scaleFactor, maskY1 * scaleFactor), (maskX2 * scaleFactor, maskY2 * scaleFactor), (maskX1 * scaleFactor, maskY2 * scaleFactor)]], dtype=np.int32)
cv2.fillPoly(mask, vertices, 255)
masked_image = cv2.bitwise_and(binary_image, mask)

cv2.imshow('Masked image', masked_image)

# morphological operations
# kernel = np.ones((2,2),np.uint8)
# masked_image = cv2.morphologyEx(masked_image, cv2.MORPH_OPEN, kernel)

cv2.imshow('morphologyEx', masked_image)

# Perform edge detection
edges = cv2.Canny(masked_image, 30, 200)

cv2.imshow('Edges', edges)

contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

print(contours)

for contour in contours:
    # Calculate the length of the contour
    length = cv2.arcLength(contour, True)

    # Calculate the area of the contour
    area = cv2.contourArea(contour)

    # Filter out small contours (adjust the area threshold as needed)
    if area > 1:
        x, y, width, height = cv2.boundingRect(contour)

        if width > 5:
            # Draw the contour on the original image
            cv2.drawContours(image_g, [contour], -1, (0, 255, 0), 2)

            # Print or store the length and area
            print(f"Length: {length}, Area: {area}")

cv2.imshow('Processed Image', image_g)
cv2.waitKey(0)
cv2.destroyAllWindows()

有没有办法更准确地检测这些线条?

python opencv computer-vision object-detection edge-detection
1个回答
0
投票

我基于图像阈值和轮廓检测提出了以下解决方案。 2 个附加滤波器的组合提供了更多可见的线条,使用阈值处理和轮廓检测更容易检测到。

def getStreakyStructuresForOneImage(imagePath, showProcessingImages, filterVertical = False):
scaleFactor = 1

maskX1 = 128  # 57
maskX2 =  355 # 263
maskY1 = 50
maskY2 = 205

angleStart = -5
angleEnd = 5

verticalKernel = np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]])
sharpenKernel2 = 0.64 * np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]])
sharpenKernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]])

if filterVertical:
    verticalKernel = np.transpose(verticalKernel)
    sharpenKernel2 = np.transpose(sharpenKernel2)
    sharpenKernel = np.transpose(sharpenKernel)

# read image
image_g = cv2.imread(imagePath, cv2.IMREAD_GRAYSCALE)

if showProcessingImages:
    cv2.imshow('Original Image', image_g)

image_blurred = applyKernel(image_g, sharpenKernel)

if showProcessingImages:
    cv2.imshow('Sharpen Image', image_blurred)

image_blurred = applyKernel(image_blurred, verticalKernel)
image_blurred = applyKernel(image_blurred, sharpenKernel2)

if showProcessingImages:
    cv2.imshow('Sharpening using different kernels', image_blurred)

_, binary_image = cv2.threshold(image_blurred, 70, 255, cv2.THRESH_BINARY)

if showProcessingImages:
    cv2.imshow('Binary image', binary_image)

# Visualize region of interest
mask = np.zeros_like(image_g)
vertices = np.array([[(maskX1 * scaleFactor, maskY1 * scaleFactor), (maskX2 * scaleFactor, maskY1 * scaleFactor),
                      (maskX2 * scaleFactor, maskY2 * scaleFactor), (maskX1 * scaleFactor, maskY2 * scaleFactor)]],
                    dtype=np.int32)
cv2.fillPoly(mask, vertices, 255)
masked_image = cv2.bitwise_and(binary_image, mask)

if showProcessingImages:
    cv2.imshow('Masked image', masked_image)

contours, _ = cv2.findContours(masked_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

image_g = cv2.cvtColor(image_g, cv2.COLOR_GRAY2RGB)
masked_image = cv2.cvtColor(masked_image, cv2.COLOR_GRAY2RGB)

line_image = np.zeros((masked_image.shape[0], masked_image.shape[1], 3), dtype=np.uint8)

numberOfMeaningfulContours = 0
totalLineLength = 0

for contour in contours:
    # Calculate the length of the contour
    length = cv2.arcLength(contour, True)

    # Calculate the area of the contour
    area = cv2.contourArea(contour)

    # Filter out small contours (adjust the area threshold as needed)
    if area > 0:
        x, y, width, height = cv2.boundingRect(contour)
        if filterVertical:
            if height > 50:
                # Draw the contour on the original image
                cv2.drawContours(line_image, [contour], -1, (0, 0, 255), 2)

                numberOfMeaningfulContours += 1;
                totalLineLength += width * mmInPx;
                # Print or store the length and area
                print(f"Length: {length}, Area: {area}")
        else:
            if width > 15:
                # Draw the contour on the original image
                cv2.drawContours(line_image, [contour], -1, (0, 0, 255), 2)

                numberOfMeaningfulContours += 1;
                totalLineLength += width * mmInPx;
                # Print or store the length and area
                print(f"Length: {length}, Area: {area}")

α = 1
β = 0.14
γ = 0

# Resultant weighted image is calculated as follows: original_img * α + img * β + γ
image_with_lines = cv2.addWeighted(image_g, α, line_image, β, γ)

if showProcessingImages:
    cv2.imshow('Processed Image', image_with_lines)

averageLineLength = 0
if numberOfMeaningfulContours > 0:
    averageLineLength = totalLineLength / numberOfMeaningfulContours

return image_with_lines, numberOfMeaningfulContours, totalLineLength, averageLineLength
© www.soinside.com 2019 - 2024. All rights reserved.