Python 处理图像有 OpenCV 库。OpenCV 可以运行在 Linux,windows,macOS 上,由 C 函数和 C++ 类构成,用于实现计算机图像、视频的编辑,应用于图像识别、运动跟踪、机器视觉等领域。
OpenCV 无法用 pip 或easy_install 安装,需要手动下载 .whl 文件安装。
实际应用中安装的OpenCV 库版本为 2.4.13。之所以用 2.4.13 版本的OpenCV,不采用 3.4 版本,是因为项目中用到的直线检测算法——霍夫变换,在OpenCV 3.4 中只能检测到一条结果,这条结果是所有线段中最长的一条。
从以下网址获取 OpenCV 的安装包(网页加载的速度比较慢,需要静候)
https://www.lfd.uci.edu/~gohlke/pythonlibs/
打开链接,待加载完毕后,搜索 OpenCV,可以找到如下页面
根据实际安装的 python 环境,下载对应的 OpenCV 安装包。
输入命令,完成 OpenCV 的安装。
pip install opencv_python‑2.4.13.5‑cp27‑cp27m‑win_amd64.whl
我对图像的处理是对灰度图像进行处理,不考虑 RGB 值,为了减轻实际的计算量。OpenCV 提供的 RGB 转灰度值的接口是
gray = cv2.cvtColor(cut_img, cv2.COLOR_BGR2GRAY)
要得到图片的线段,在灰度图上继续做后续操作。对图像线段进行检测的算法,是通过算子进行边缘检测,常用算子有Prewitt 算子、sobel 算子、Laplace 算子以及 Canny 算子。这里就不对这些算子的检测结果进行详述了,因为我也不是很明白几种算子实际效果的区别。本例中使用的是 canny 算子。
OpenCV 中调用 canny 算子的方式如下:
edge = cv2.Canny(image, threshold1,threshold2[, edges[, apertureSize[, L2gradient ]]])
必要参数:
第一个参数是需要处理的原图像,该图像必须为单通道的灰度图;
第二个参数是阈值1;
第三个参数是阈值2。
其中较大的阈值2用于检测图像中明显的边缘,但一般情况下检测的效果不会那么完美,边缘检测出来是断断续续的。所以这时候用较小的第一个阈值用于将这些间断的边缘连接起来。
可选参数中apertureSize就是Sobel算子的大小。而L2gradient参数是一个布尔值,如果为真,则使用更精确的L2范数进行计算(即两个方向的倒数的平方和再开放),否则使用L1范数(直接将两个方向导数的绝对值相加)。
随后进行霍夫变换,得到线段的坐标。
霍夫变换是经典的检测直线的算法。其最初用来检测图像中的直线,同时也可以将其扩展,以用来检测图像中简单的结构。
cv2.HoughLines函数输出的是[float,float]形式的ndarray,其中每个值表示检测到的线(ρ , θ)中浮点点值的参数。在本例中,函数将通过步长为1的半径和步长为π/180的角来搜索所有可能的直线。然而用这种方法检测出来的是贯穿整张图像的直线,需要检测线段时,则要采用概率霍夫变换。
lines = cv2.HoughLinesP(edges, 1,np.pi/180, 80, minLineLength, maxLineGap)
HoughLinesP(image, rho, theta, threshold, lines=None, minLineLength=None, maxLineGap=None)
最后将找到的线段均显示在图片上,即完成直线检测。
由于图片画质因素,很多看似是直线的也未检测出是直线,还有许多散碎的直线并没有连接成长线。有待后续研究解决。
import cv2
import numpy as np
#img = cv2.imread("C:/AE_PUFF/python_vision/2018_04_27/kk-3.jpg")
img = cv2.imread("test.jpg")
cv2.imshow('origin_img', img)
height = img.shape[0] # 高度
width = img.shape[1] # 宽度
cut_img = img
gray = cv2.cvtColor(cut_img, cv2.COLOR_BGR2GRAY)
cv2.imshow('gray_img', gray)
cv2.waitKey(0)
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
# lines = cv2.HoughLines(edges, 1, np.pi / 180, 110)
result = cut_img.copy()
# lines1 = lines[:, 0, :] # 提取为为二维
# for rho, theta in lines1[:]:
# a = np.cos(theta)
# b = np.sin(theta)
# x0 = a * rho
# y0 = b * rho
# x1 = int(x0 + 1000 * (-b))
# y1 = int(y0 + 1000 * a)
# x2 = int(x0 - 1000 * (-b))
# y2 = int(y0 - 1000 * a)
# cv2.line(result, (x1, y1), (x2, y2), (255, 0, 0), 1)
minLineLength = 70 # height/32
maxLineGap = 2 # height/40
lines = cv2.HoughLinesP(edges, 1, np.pi / 180, 50, minLineLength=minLineLength, maxLineGap=maxLineGap)
for x1, y1, x2, y2 in lines[0]:
cv2.line(result, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.imshow('result', result)
cv2.waitKey(0)
import cv2
import numpy as np
img = cv2.imread("origin.jpg")
# cv2.imshow('origin_img', img)
height = img.shape[0] # 高度
width = img.shape[1] # 宽度
cut_img = img
gray = cv2.cvtColor(cut_img, cv2.COLOR_BGR2GRAY)
# cv2.imshow('gray_img', gray)
# cv2.waitKey(0)
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
# lines = cv2.HoughLines(edges, 1, np.pi / 180, 110)
result = cut_img.copy()
# lines1 = lines[:, 0, :] # 提取为为二维
# for rho, theta in lines1[:]:
# a = np.cos(theta)
# b = np.sin(theta)
# x0 = a * rho
# y0 = b * rho
# x1 = int(x0 + 1000 * (-b))
# y1 = int(y0 + 1000 * a)
# x2 = int(x0 - 1000 * (-b))
# y2 = int(y0 - 1000 * a)
# cv2.line(result, (x1, y1), (x2, y2), (255, 0, 0), 1)
minLineLength = 5 # height/32
maxLineGap = 0 # height/40
lines = cv2.HoughLinesP(edges, 1, np.pi / 180, 80, minLineLength=minLineLength, maxLineGap=maxLineGap)
line1 = lines[:, 0, :] # 提取为二维
for x1, y1, x2, y2 in line1[:]:
cv2.line(result, (x1, y1), (x2, y2), (0, 255, 0), 1)
cv2.imshow('result', result)
cv2.waitKey(0)
区别是
line1 = lines[:, 0, :] # 提取为二维