opencv 从背景(墙)中检测前景(岩石/堆料),以找到材料Angular

k2arahey  于 2023-05-29  发布在  Angular
关注(0)|答案(4)|浏览(166)

我试图找到Angular 的库存(在左侧和右侧),通过使用大津阈值分割图像。我的形象是这样的:

在代码中,我将其分段,并找到图像中的第一个黑色像素

分割的照片似乎没有任何黑色像素在白色背景,但随后它检测到一些黑色像素,即使我已经使用morphology.opening

如果我用不同的图像,它似乎没有这个问题

如何解决此问题?有什么想法吗?(下一步是找到左手边和右手边的Angular )
代码附在这里

from skimage import io, filters, morphology, measure
import numpy as np
import cv2 as cv
from scipy import ndimage
import math
# Load the image
image = io.imread('mountain3.jpg', as_gray=True)

# Apply Otsu's thresholding to segment the image
segmented_image = image > filters.threshold_otsu(image)

# Perform morphological closing to fill small gaps
structuring_element = morphology.square(1)
closed_image = morphology.closing(segmented_image, structuring_element)

# Apply morphological opening to remove small black regions in the white background
structuring_element = morphology.disk(10)  # Adjust the disk size as needed
opened_image = morphology.opening(closed_image, structuring_element)

# Fill larger gaps using binary_fill_holes
#filled_image = measure.label(opened_image)
#filled_image = filled_image > 0

# Display the segmented image after filling the gaps
io.imshow(opened_image)

io.show()
# Find the first row containing black pixels
first_black_row = None
for row in range(opened_image.shape[0]):
    if np.any(opened_image[row, :] == False):
        first_black_row = row
        break

if first_black_row is not None:
    edge_points = []  # List to store the edge points

    # Iterate over the rows below the first black row
    for row in range(first_black_row, opened_image.shape[0]):
        black_pixel_indices = np.where(opened_image[row, :] == False)[0]

        if len(black_pixel_indices) > 0:
            # Store the first black pixel coordinates on the left and right sides
            left_x = black_pixel_indices[0]
            right_x = black_pixel_indices[-1]
            y = row

            # Append the edge point coordinates
            edge_points.append((left_x, y))
            edge_points.append((right_x, y))

    if len(edge_points) > 0:
        # Plotting the edge points
        import matplotlib.pyplot as plt

        edge_points = np.array(edge_points)

        plt.figure()
        plt.imshow(opened_image, cmap='gray')
        plt.scatter(edge_points[:, 0], edge_points[:, 1], color='red', s=1)
        plt.title('Edge Points')
        plt.show()
    else:
        print("No edge points found.")
else:
    print("No black pixels found in the image.")
fnvucqvd

fnvucqvd1#

你的问题是图像有噪音。你需要处理噪音。
这通常是通过某种低通来完成的,即。模糊。我建议用中值模糊。
下面是中值滤波器的结果,内核大小为9:

以及与源的每像素绝对差,在振幅上放大20倍:

(this建议你可以做一个带通来捕捉“纹理”的堆与平坦的背景)
下面是大津阈值处理(和反演)之后的图片:

你的前景和背景几乎没有对比。如果你有更好的对比背景,这将不是一个大问题。
下面是基于色调的阈值化,因为背景和前景在色调上 * 略有 * 不同:

形态学关闭:

为了获得库存的左、右斜率的线条,您需要一些处理轮廓或边缘像素的东西。
轮廓查找和连接组件标注都有这个问题,这就是为什么推荐这些答案的答案也必须建议明确过滤结果以去除小碎片(噪声)。
因此,这些方法(轮廓/CC)并不 * 解决噪声的问题,它们只是将其转换成一个不同的问题,在这个问题中,您仍然需要处理噪声(通过过滤它),就在 * 处理图像之后。
我建议早点处理噪音。

x6h2sr28

x6h2sr282#

答:这个答案目前还不完整。它假设岩石堆不可能分裂成瑞士奶酪,因此轮廓可能无法准确地抓住前景。参见Christoph拉克维茨的答案,以更可靠的方式区分前景和背景,我的答案解释了如何计算所涉及的Angular 。
Contours是你要找的。你会想抓住图像中最大的轮廓,它会忽略背景中的小像素。然后你要得到这个轮廓,找到轮廓上的最高点,然后找到轮廓的最高部分,它们存在于图像的最左边和最右边,这就是你如何画三角形。从那里你可以确定Angular 。

import cv2
import numpy as np

# Load images
img = cv2.imread('image.jpg')

# Convert image to grayscale for easier thresholding
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Threshold the image
_, thresh = cv2.threshold(gray, 110, 255, cv2.THRESH_BINARY_INV)

# Find contours
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# Find the largest chunk of black pixels
max_contour = max(contours, key=cv2.contourArea)

# Draw contour on a copy of the image for visualization
img_contours = img.copy()
cv2.drawContours(img_contours, [max_contour], -1, (255, 0, 0), 3)
cv2.waitKey(0)

cv2.imwrite("output.jpg", img_contours)

area = cv2.contourArea(max_contour)

# For highest Y value (max_contour)
max_val_y = min(max_contour, key=lambda coord: coord[0][1])

# For the highest Y values at the leftmost and rightmost sides of the contour
sorted_contour = sorted(max_contour, key=lambda coord: coord[0][0])  # Sort by X values

# Find the Y value of the highest point in the leftmost side
left_sub_contour = [point for point in sorted_contour if point[0][0] == sorted_contour[0][0][0]]
leftmost_val_y = min(left_sub_contour, key=lambda coord: coord[0][1])[0][1]

# Find the Y value of the highest point in the rightmost side
right_sub_contour = [point for point in sorted_contour if point[0][0] == sorted_contour[-1][0][0]]
rightmost_val_y = min(right_sub_contour, key=lambda coord: coord[0][1])[0][1]

# Get the x, y coordinates for the highest point of the max_contour
highest_point = tuple(max_val_y[0])

# Get the x, y coordinates for the highest point at the leftmost side
leftmost_point = (sorted_contour[0][0][0], leftmost_val_y)

# Get the x, y coordinates for the highest point at the rightmost side
rightmost_point = (sorted_contour[-1][0][0], rightmost_val_y)

# Draw the triangle
triangle_contour = np.array([highest_point, leftmost_point, rightmost_point])
cv2.drawContours(img, [triangle_contour], 0, (0, 255, 0), 2)  # Change (0, 255, 0) to the color you want for the triangle

# Save the image
cv2.imwrite("output_triangle.jpg", img)

# Calculate distances between points (sides of triangle)
a = np.linalg.norm(np.array(highest_point)-np.array(rightmost_point))
b = np.linalg.norm(np.array(highest_point)-np.array(leftmost_point))
c = np.linalg.norm(np.array(rightmost_point)-np.array(leftmost_point))

# Calculate angle using the law of cosines
cos_angle = (a**2 + b**2 - c**2) / (2*a*b)
angle_rad = np.arccos(cos_angle)  # Angle in radians
angle_deg = np.degrees(angle_rad)  # Convert to degrees

# Calculate angle at the left side of the triangle
cos_left_angle = (b**2 + c**2 - a**2) / (2*b*c)
left_angle_rad = np.arccos(cos_left_angle)  # Angle in radians
left_angle_deg = np.degrees(left_angle_rad)  # Convert to degrees

# Calculate angle at the right side of the triangle
right_angle_deg = 180 - (left_angle_deg + angle_deg)

print(f"Angle at the left side of the triangle: {left_angle_deg} degrees")
print(f"Angle at the right side of the triangle: {right_angle_deg} degrees")

print(f"Angle at the highest point of the triangle: {angle_deg} degrees")
print("Area of the rock pile:", area)

输入:

输出:

Angle at the left side of the triangle: 23.59662632252385 degrees
Angle at the right side of the triangle: 19.929564001732047 degrees
Angle at the highest point of the triangle: 136.4738096757441 degrees
Area of the rock pile: 1868530.0

我最近读了一本叫Designing Autonomous AI的书,书中提到了制作PID controllers来管理一个岩堆大小。出于好奇,你是在做吗?
也相关:如何将分割结果与另一幅图像叠加?

bttbmeg0

bttbmeg03#

首先,您可以使用Connected Components(connectedComponentsWithStats)移除任何面积小于阈值的斑点(移除那些黑点)。
之后,您可以将图像分为2(在中间垂直拆分)。并对这些图像中的每一个进行np.polyfit(度为1)(使用红色点)。
有了斜率,你就能找到Angular 。

vltsax25

vltsax254#

如果你已经有了一个一维y值序列(每列的y值),并且只有少数值是你想要去除的噪声...
考虑检查原始序列和模糊序列之间的差异。
您将能够删除只有数据有很大的差异。

示例:

这是原始的y值序列(显示为红点)。
这看起来和你的情况一样。

而“模糊序列”在下面显示为绿色。
(我只是使用了内核大小为15的OpenCV's blur() function

这就是结果。删除的点的颜色为青色。

相关问题