0

我正在尝试使用 cv2 检测纸张上的颗粒状打印线。我需要线条的角度。我在图像处理方面没有太多知识,我只需要检测线。我尝试使用参数,但总是检测到角度错误。有人可以帮助我。这是我的代码:

import cv2
import numpy as np
import matplotlib.pylab as plt
from matplotlib.pyplot import figure

img = cv2.imread('CamXY1_1.bmp')
crop_img = img[100:800, 300:900]
blur = cv2.GaussianBlur(crop_img, (1,1), 0)
ret,thresh = cv2.threshold(blur,150,255,cv2.THRESH_BINARY)
gray = cv2.cvtColor(thresh,cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 60, 150)

figure(figsize=(15, 15), dpi=150)
plt.imshow(edges, 'gray')

lines = cv2.HoughLines(edges,1,np.pi/180,200)
for rho,theta in lines[0]:
    a = np.cos(theta)
    b = np.sin(theta)
    x0 = a*rho
    y0 = b*rho
    x1 = int(x0 + 3000*(-b))
    y1 = int(y0 + 3000*(a))
    x2 = int(x0 - 3000*(-b))
    y2 = int(y0 - 3000*(a))

    cv2.line(img,(x1,y1),(x2,y2),(0, 255, 0),2)

待检测图像

4

1 回答 1

1

这是在不使用霍夫线变换的情况下估计线(及其角度)的可能解决方案。这个想法是使用reduce函数定位线的起点和终点。此功能可以将图像缩小为单列或单行。如果我们缩小图像,我们还可以获得SUM缩小图像中所有像素的总和。使用此信息,我们可以估计线的极值点并计算其角度。这是步骤:

  1. 调整图像大小,因为它太大了
  2. 通过自适应阈值获取二值图像
  3. 定义图像的两个极端区域并裁剪它们
  4. 使用模式将 ROI减少为一列SUM,即所有行的总和
  5. 累计超过阈值的总值
  6. 估计线的起点和终点
  7. 获取线的角度

这是代码:

# imports:
import cv2
import numpy as np
import math

# image path
path = "D://opencvImages//"
fileName = "mmCAb.jpg"

# Reading an image in default mode:
inputImage = cv2.imread(path + fileName)

# Scale your BIG image into a small one:
scalePercent = 0.3

# Calculate the new dimensions
width = int(inputImage.shape[1] * scalePercent)
height = int(inputImage.shape[0] * scalePercent)
newSize = (width, height)

# Resize the image:
inputImage = cv2.resize(inputImage, newSize, None, None, None, cv2.INTER_AREA)

# Deep copy for results:
inputImageCopy = inputImage.copy()

# Convert BGR to grayscale:
grayInput = cv2.cvtColor(inputImage, cv2.COLOR_BGR2GRAY)

# Adaptive Thresholding:
windowSize = 51
windowConstant = 11
binaryImage = cv2.adaptiveThreshold(grayInput, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, windowSize, windowConstant)

第一步是获取二值图像。请注意,我之前缩小了您的输入,因为它太大了,我们不需要所有这些信息。这是二进制掩码:

现在,我们不需要大部分图像。事实上,由于这条线横跨整个图像,我们只能“修剪”第一列和最后一列并检查白色像素的开始位置。不过,我会将一列裁剪得更宽一些,这样我们就可以确保我们有足够的数据和尽可能少的噪音。我将定义两个感兴趣区域(ROI) 并裁剪它们。然后,我将使用该SUM模式将每个 ROI 减少为一列,这将为我提供每行所有强度的总和。之后,我可以累积总和超过某个阈值的位置并近似线的位置,如下所示:

# Define the regions that will be cropped
# from the original image:
lineWidth = 5
cropPoints = [(0, 0, lineWidth, height), (width-lineWidth, 0, lineWidth, height)]

# Store the line points here:
linePoints = []

# Loop through the crop points and 
# crop de ROI:

for p in range(len(cropPoints)):

    # Get the ROI:
    (x,y,w,h) = cropPoints[p]

    # Crop the ROI:
    imageROI = binaryImage[y:y+h, x:x+w]

    # Reduce the ROI to a n row x 1 columns matrix:
    reducedImg = cv2.reduce(imageROI, 1, cv2.REDUCE_SUM, dtype=cv2.CV_32S)

    # Get the height (or lenght) of the arry:
    reducedHeight = reducedImg.shape[0]

    # Define a threshold and accumulate
    # the coordinate of the points:
    threshValue = 100
    pointSum = 0
    pointCount = 0

    for i in range(reducedHeight):
        currentValue = reducedImg[i]

        if currentValue > threshValue:
            pointSum = pointSum + i
            pointCount = pointCount + 1

    # Get average coordinate of the line:
    y = int(accX / pixelCount)
    # Store in list:
    linePoints.append((x, y))

红色矩形显示了我从输入图像中裁剪的区域:

请注意,我已将两个点都存储在linePoints列表中。让我们通过绘制一条连接两个点的线来检查我们的近似值:

# Get the two points:
p0 = linePoints[0]
p1 = linePoints[1]

# Draw the line:
cv2.line(inputImageCopy, (p0[0], p0[1]), (p1[0], p1[1]), (255, 0, 0), 1)
cv2.imshow("Line", inputImageCopy)
cv2.waitKey(0)

产生:

还不错吧?现在我们有了两个点,我们可以估计这条线的角度:

# Get angle:
adjacentSide = p1[0] - p0[0]
oppositeSide = p0[1] - p1[1]

# Compute the angle alpha:
alpha = math.degrees(math.atan(oppositeSide / adjacentSide))

print("Angle: "+str(alpha))

这打印:

Angle: 0.534210901840831
于 2021-05-11T07:23:27.853 回答