- 前言
- Day3.1 OpenCV图像处理核心技术(上篇)
- 颜色处理与二值化
- 颜色空间转换
- 阈值二值化
- 颜色阈值筛选
- 滤波去噪
- 形态学操作
- 腐蚀膨胀
- 开运算与闭运算
- 边缘检测
- Sobel算子
- Roberts算子
- Prewitt算子
- Canny算子
- Laplace算子
- 边缘检测测试代码
- 结语
按照OpenCV官方doc顺序来进行学习回忆和总结,本次的内容是opencv中的图像处理核心技术(imgproc.hpp),包括
- 颜色处理与二值化
- 滤波去噪
- 形态学操作
- 边缘检测
图像处理常用的颜色空间有RGB、HSV、YCbCr、GRAY等
颜色空间转换使用cv2.cvtColor(img, type)进行颜色空间的转换,需要注意的是imread默认读取图片为BGR
import cv2
import numpy as np
if __name__ == '__main__':
img = cv2.imread('starry_night.png')
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imshow('img', img)
cv2.imshow('img_hsv', img_hsv)
cv2.imshow('img_gray', img_gray)
cv2.waitKey(0)
阈值二值化
使用cv2.threshold()进行图像二值化,
需要注意,src必须是单通道图,thresh是阈值,二值化后像素值为{0, maxval},常用的type有 cv2.THRESH_BINARY cv2.THRESH_BINARY_INV cv2.THRESH_OTSU
# ret, dst = cv2.threshold(src, thresh, maxval, type)
cv2.threshold(img_gray, 100, 255, cv2.THRESH_BINARY)
此外,还有自适应阈值二值化方法,
# cv2.adaptiveThreshold(src, maxval, method, type, blocksize, C)
dst = cv2.adaptiveThreshold(src, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 3, 10)
blocksize是分割计算的区域大小,必须是奇数 C表示每个区域计算出阈值后,减去这个常数作为这个区域的最终阈值
颜色阈值筛选使用cv2.inRange()进行颜色阈值筛选,其中lowerthres和upperthres分别表示长度与图片通道数相同的阈值数组
# mask = cv2.inRange(img, lowerthres, upperthres)
mask = cv2.inRange(img_hsv, np.array([100, 40, 40]), np.array([120, 240, 240]))
输入RGB: 转到HSV空间:
HSV空间颜色提取:
常用的图像滤波方法包括均值滤波,中值滤波,高斯滤波,双边滤波等 均值滤波将中心附近像素累计平均后作为中心值,滤波后图像变模糊 中值滤波取中心附近像素值的中值,适合去除椒盐噪声 高斯滤波采用高斯核进行加权平均,适合去除高斯噪声 双边滤波在滤波时能保持边缘的清晰度
# 均值滤波cv2.blur(img, Ksize)
img_blur = cv2.blur(img, (3, 3))
# 中值滤波cv2.medianBlur(img, Ksize)
img_median_blur = cv2.medianBlur(img, 3)
# 高斯滤波cv2.GaussianBlur(img, Ksize, Sigma)
img_gauss_blur = cv2.GaussianBlur(img, (3, 3), 0)
# 双边滤波cv2.bilateralFilter(img, d, sigmaColor, sigmaSpace)
img_bliteral_blur = cv2.bilateralFilter(img, 9, 20, 45)
形态学操作
在图像处理,如二值化、颜色筛选后,目标图案被截断需要截断时,可以通过形态学操作进行填充和削减
腐蚀膨胀主要针对二值图像,腐蚀是将图像中黑色区域进一步“吞噬”白色区域,膨胀则是让白色区域“吞噬”黑色区域。
在形态学操作前,首先要获得“吞噬”的模板,即核的形状,可选的形状有 cv2.MORPH_RECT cv2.MORPH_CROSS cv2.MORPH_ELLIPSE 也可以通过numpy数组自定义核形状
# 获得核 cv2.getStructuringElement(type, Ksize)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
# or 自定义核形状
kernel_np = np.ones([3, 3], np.uint8)
# 腐蚀 cv2.erode(img, kernel, iterations=None)
img_erode = cv2.erode(img, kernel)
# 膨胀 cv2.dilate(img, kernel)
img_dilate = cv2.dilate(img, kernel)
开运算与闭运算
先腐蚀将图像中相连的部分截断,再膨胀的操作是开运算 先膨胀将图像中断开的部分连接,再腐蚀的操作是闭运算
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
# 开运算
img_open = cv2.morphologyEx(src, cv2.MORPH_OPEN, kernel)
# 闭运算
img_close = cv2.morphologyEx(src, cv2.MORPH_CLOSE, kernel)
边缘检测
传统边缘检测算法主要采用一阶微分算子Sobel,Roberts,Prewitt等以及二阶算子Canny,Laplace等
Sobel算子def edge_Sobel(img):
# Sobel 算子
x = cv2.Sobel(img, cv2.CV_16S, 1, 0)
y = cv2.Sobel(img, cv2.CV_16S, 0, 1)
# 转 uint8 ,图像融合
X = cv2.convertScaleAbs(x)
Y = cv2.convertScaleAbs(y)
img_sobel = cv2.addWeighted(X, 0.5, Y, 0.5, 0)
return img_sobel
Roberts算子
def edge_Roberts(img):
# Roberts 算子
kernelx = np.array([[-1, 0], [0, 1]], dtype=int)
kernely = np.array([[0, -1], [1, 0]], dtype=int)
x = cv2.filter2D(img, cv2.CV_16S, kernelx)
y = cv2.filter2D(img, cv2.CV_16S, kernely)
X = cv2.convertScaleAbs(x)
Y = cv2.convertScaleAbs(y)
img_roberts = cv2.addWeighted(X, 0.5, Y, 0.5, 0)
return img_roberts
Prewitt算子
def edge_Prewitt(img):
# Prewitt 算子
kernelx = np.array([[1, 1, 1], [0, 0, 0], [-1, -1, -1]], dtype=int)
kernely = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]], dtype=int)
x = cv2.filter2D(img, cv2.CV_16S, kernelx)
y = cv2.filter2D(img, cv2.CV_16S, kernely)
# 转 uint8 ,图像融合
X = cv2.convertScaleAbs(x)
Y = cv2.convertScaleAbs(y)
img_prewitt = cv2.addWeighted(X, 0.5, Y, 0.5, 0)
return img_prewitt
Canny算子
def edge_canny(img):
# Canny
dst = cv2.Canny(img, 45, 90)
img_canny = dst
return img_canny
Laplace算子
def edge_laplace(img):
# Laplacian
dst = cv2.Laplacian(img, cv2.CV_16S, ksize=3)
img_laplace = cv2.convertScaleAbs(dst)
return img_laplace
边缘检测测试代码
import cv2
import numpy as np
def edge_Roberts(img):
# Roberts 算子
kernelx = np.array([[-1, 0], [0, 1]], dtype=int)
kernely = np.array([[0, -1], [1, 0]], dtype=int)
x = cv2.filter2D(img, cv2.CV_16S, kernelx)
y = cv2.filter2D(img, cv2.CV_16S, kernely)
X = cv2.convertScaleAbs(x)
Y = cv2.convertScaleAbs(y)
img_roberts = cv2.addWeighted(X, 0.5, Y, 0.5, 0)
return img_roberts
def edge_Prewitt(img):
# Prewitt 算子
kernelx = np.array([[1, 1, 1], [0, 0, 0], [-1, -1, -1]], dtype=int)
kernely = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]], dtype=int)
x = cv2.filter2D(img, cv2.CV_16S, kernelx)
y = cv2.filter2D(img, cv2.CV_16S, kernely)
# 转 uint8 ,图像融合
X = cv2.convertScaleAbs(x)
Y = cv2.convertScaleAbs(y)
img_prewitt = cv2.addWeighted(X, 0.5, Y, 0.5, 0)
return img_prewitt
def edge_Sobel(img):
# Sobel 算子
x = cv2.Sobel(img, cv2.CV_16S, 1, 0)
y = cv2.Sobel(img, cv2.CV_16S, 0, 1)
# 转 uint8 ,图像融合
X = cv2.convertScaleAbs(x)
Y = cv2.convertScaleAbs(y)
img_sobel = cv2.addWeighted(X, 0.5, Y, 0.5, 0)
return img_sobel
def edge_laplace(img):
# Laplacian
dst = cv2.Laplacian(img, cv2.CV_16S, ksize=3)
img_laplace = cv2.convertScaleAbs(dst)
return img_laplace
def edge_canny(img):
# Canny
dst = cv2.Canny(img, 45, 90)
img_canny = dst
return img_canny
if __name__ == "__main__":
img = cv2.imread('distorted.png')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, img_thres = cv2.threshold(img_gray, 100, 255, cv2.THRESH_BINARY)
img_thres = img_gray
img_roberts = edge_Roberts(img_thres)
img_prewitt = edge_Prewitt(img_thres)
img_sobel = edge_Sobel(img_thres)
img_laplace = edge_laplace(img_thres)
img_canny = edge_canny(img_thres)
cv2.imshow('img_roberts', img_roberts)
cv2.imshow('img_prewitt', img_prewitt)
cv2.imshow('img_sobel', img_sobel)
cv2.imshow('img_laplace', img_laplace)
cv2.imshow('img_canny', img_canny)
cv2.waitKey(0)
输入: Roberts:
Prewitt:
Sobel:
Laplace:
Canny:
本次总结回顾了OpenCV图像处理中的核心操作,包括颜色处理,滤波去噪,腐蚀膨胀开闭运算,边缘检测算子。下次将继续学习轮廓操作、霍夫变换和直方图操作。