问题描述
我正在尝试使用 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)
解决方法
这是一种无需使用霍夫线变换即可估计直线(及其角度)的可能解决方案。这个想法是使用 reduce 函数定位线的起点和终点。此功能可以将图像缩小为单列或单行。如果我们缩小图像,我们还可以获得缩小图像中所有像素的总SUM
。使用此信息,我们可以估计直线的极值点并计算其角度。这是步骤:
- 调整图片的大小,因为它太大了
- 通过自适应阈值 获取二值图像
- 定义图像的两个极端区域并裁剪它们
-
使用
SUM
模式将ROI减少到一列,即所有行的总和 - 累积超过阈值的总值
- 估计线的起点和终点
- 获取线的角度
代码如下:
# 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