前言:
欢迎来到本博客
目前正在进行 OpenCV技能树的学习,OpenCV是学习图像处理理论知识比较好的一个途径,至少比看书本来得实在。本专栏文章主要记录学习OpenCV的过程以及对学习过程的一些反馈记录。感兴趣的同学可以一起学习、一起交流、一起进步。
下面框架图主要是OpenCV入门技能树总共有27个知识点,其中包括了8个大章的学习内容,如果感兴趣的可以进一步学习博主写的关于OpenCV的专栏【通俗易懂OpenCV(C++版)详细教程】:
支持:如果觉得博主的文章还不错或者您用得到的话,可以悄悄关注一下博主哈,如果三连收藏支持就更好啦!这就是给予我最大的支持!
题目:图像的灰度化处理
按照颜色对图像进行分类,可以分为:彩色图像、灰度图像和二值图像。
灰度图像是只含亮度信息,不含色彩信息的图像。
灰度化处理是把彩色图像转换为灰度图像的过程,是图像处理中的基本操作。
OpenCV中彩色图像使用 BGR 格式。灰度图像中用 8bit 数字 0~255 表示灰度,如:0 表示纯黑,255 表示纯白。
彩色图像进行灰度化处理,可以在读取图像文件时直接读取为灰度图像,也可以通过函数 cv.cvtColor() 将彩色图像转换为灰度图像。
以下图像灰度化程序中,能够正确执行图像灰度化操作的是?
解析:
方法一:imread时直接转灰度
cv.imread("GrayscaleLena.tif", cv.IMREAD_GRAYSCALE);
方法二: 用cvtColor
img = cv.imread("GrayscaleLena.tif")
imgGray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
import cv2 as cv
if __name__ == '__main__':
img = cv.imread("GrayscaleLena.tif")
imgGray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
cv.imshow("ImgGray", imgGray)
key = cv.waitKey(0)
题目:普通鸬鹚不普通
OpenCV的膨胀与腐蚀,让“普通鸬鹚”不普通,下图从左到右分别是:
原图
原图经过腐蚀得到的图
原图经过膨胀得到的图
解析:
import numpy as np
import cv2
if __name__ == '__main__':
img_origin = cv2.imread('bird.jpeg', cv2.COLOR_BGR2LAB)//BGR颜色空间与CIE Lab颜色空间之间的相互转换
kernel = np.ones((3, 3), np.uint8)//卷积核
img_erosion = cv2.erode(img_origin, kernel, iterations=1)//腐蚀 注意参数顺序 卷积核参数在第二个参数处
img_dilation = cv2.dilate(img_origin, kernel, iterations=1)//膨胀 注意卷积核参数在第二个参数处
img_all = np.concatenate((img_origin, img_erosion, img_dilation), axis=1)//三幅图像合成成一张图像
cv2.imshow('img: origin, erosion and dilation', img_all)
cv2.waitKey(0)
cv2.destroyAllWindows()
题目:小鸊鷉橡皮擦(I)
OpenCV 里先腐蚀再膨胀操作叫做“开运算”。
小鸊鷉(pi ti)的名片被小朋友画了几笔,尝试通过先腐蚀再膨胀修复,效果不明显
框架代码如下,给出开运算函数def open_op(img)
:
import numpy as np
import cv2
def open_op(img):
# TODO(You): 请在此实现代码
if __name__ == '__main__':
img_origin = cv2.imread('bird.png', cv2.COLOR_BGR2LAB)
img_opened = open_op(img_origin)
img_all = np.concatenate((img_origin, img_opened), axis=1)
cv2.imwrite('img_opened.jpeg', img_all)
cv2.imshow('img: origin, erosion and dilation', img_all)
cv2.waitKey(0)
cv2.destroyAllWindows()
解析:
def open_op(img):
kernel = np.ones((3, 3), np.uint8))//卷积核,是下列两个函数的第二个参数
img1 = cv2.erode(img, kernel, iterations=1)//对img图像进行腐蚀,得到img1
img2 = cv2.dilate(img1, kernel, iterations=1)//对img1进行膨胀,得到img2
return img2//整个函数应该返回img2
题目:小鸊鷉橡皮擦(II)
OpenCV 里先膨胀再腐蚀操作叫做“闭运算”。
小鸊鷉(pi ti)的名片被小朋友画了几笔,尝试通过先膨胀再腐蚀修复,完成任务
框架代码如下,给出开运算函数def close_op(img):
:
import numpy as np
import cv2
def close_op(img):
# TODO(You): 请在此实现代码
if __name__ == '__main__':
img_origin = cv2.imread('bird.png', cv2.COLOR_BGR2LAB)
img_closed = close_op(img_origin)
img_all = np.concatenate((img_origin, img_opened), axis=1)
cv2.imwrite('img_opened.jpeg', img_all)
cv2.imshow('img: origin, erosion and dilation', img_all)
cv2.waitKey(0)
cv2.destroyAllWindows()
解析:
def close_op(img):
kernel = np.ones((3, 3), np.uint8)//卷积核,是下列两个函数的第二个参数
img1 = cv2.dilate(img, kernel, iterations=1)//对img进行膨胀,得到img1
img2 = cv2.erode(img1, kernel, iterations=1)//对img1图像进行腐蚀,得到img2
return img2//整个函数应该返回img2
题目:框住水鸭子
OpenCV 里的连通区域分析可以将具有相同像素值且位置相邻的前景像素点组成的图像区域识别出来。有两种像素相邻的定义:
通过OpenCV的连通区域分析算法,我们可以将下图的水鸭子的外框框出来:
框架代码如下:
import cv2
import numpy as np
import matplotlib.pyplot as plt
def close_op(img):
kernel = np.ones((3, 3), np.uint8)
img1 = cv2.dilate(img, kernel, iterations=1)
img2 = cv2.erode(img1, kernel, iterations=1)
return img2
def show_images(images):
i = 0
for title in images:
plt.subplot(2, 3, i+1), plt.imshow(images[title], 'gray')
plt.title(title)
plt.xticks([]), plt.yticks([])
i += 1
plt.show()
if __name__ == '__main__':
duck_origin = cv2.imread('duck.jpeg', -1)
duck_box = duck_origin.copy()
duck_gray = cv2.cvtColor(duck_box, cv2.COLOR_BGR2GRAY)
duck_gray_with_closed = close_op(duck_gray)
ret, duck_binary = cv2.threshold(duck_gray_with_closed, 127, 255, cv2.THRESH_BINARY)
# TODO(You): 请实现识别鸭子区域并用框出来的代码
images = {
'duck_origin': duck_origin,
'duck_gray': duck_gray,
'duck_gray_with_closed_op': duck_gray_with_closed,
'duck_binary': duck_binary,
'duck_box': duck_box
}
show_images(images)
解析:
ret, labels, stats, centroid = cv2.connectedComponentsWithStats(duck_binary)
duck_area = sorted(stats, key=lambda s: s[-1], reverse=False)[-2]
cv2.rectangle(
duck_box,
(duck_area[0], duck_area[1]),
(duck_area[0] + duck_area[2], duck_area[1] + duck_area[3]),
(255, 0, 0),
3)
connectedComponentsWithStats函数介绍
num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(image, connectivity=8, ltype=None)
参数介绍如下:
image:也就是输入图像,必须是二值图,即8位单通道图像。(因此输入图像必须先进行二值化处理才能被这个函数接受)
connectivity:可选值为4或8,也就是使用4连通还是8连通。
ltype:输出图像标记的类型,目前支持CV_32S 和 CV_16U。
返回值:
num_labels:所有连通域的数目
labels:图像上每一像素的标记,用数字1、2、3…表示(不同的数字表示不同的连通域)
stats:每一个标记的统计信息,是一个5列的矩阵,每一行对应每个连通区域的外接矩形的x、y、width、height和面积,示例如下: 0 0 720 720 291805
centroids:连通域的中心点
题目:寻找图像的轮廓
轮廓是由连续的点组成的曲线。轮廓与边缘很相似,但轮廓是连续的,边缘不一定都连续。
轮廓反映了物体的基本外形,常用于形状分析和物体的检测和识别。
OpenCV 提供函数 cv.findContours() 对二值图像寻找轮廓,函数 cv2.drawContours() 绘制轮廓。
函数说明:
cv.findContours(image, mode, method[, contours[, hierarchy[, offset]]] ) → contours, hierarchy
我们从康熙御笔书法中提取轮廓,据说这是一种经典的书画复制技法。
下图从上到下分别是: * 康熙御笔原图 * 康熙御笔碑帖图 * 康熙御笔轮廓图
解析:
import cv2 as cv
if __name__ == '__main__':
img = cv.imread("Contours.jpg", flags=1)
imgGray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(imgGray, 127, 255, cv.THRESH_BINARY_INV)
contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
contourPic = cv.drawContours(img, contours, -1, (0, 0, 255), 2)
cv.imshow("ContourPicture", contourPic)
cv.waitKey(0)