如何检测颗粒线?

问题描述

我正在尝试使用 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,0),2)

imagetobedetected

解决方法

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

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

代码如下:

# 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,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,lineWidth,height),(width-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),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

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...