本文是基于哔哩哔哩OpenCV入门课程的内容加上我个人的理解而来。 本篇文章的主要内容: 阅读本篇文章,你需要具备python的基本语法的学习。如果你并没有学习过python,可以去看我的python专栏:python
本文的内容:图片的读取,RGB彩色通道,区域裁剪,绘制图像1和文字,均值滤波,特征提取,模板匹配,梯度算法,阈值算法,形态学操作,摄像头的读取。
OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习库,最初由英特尔于1999年发布,现已成为计算机视觉领域的一个重要工具。它以C++为核心语言开发,并提供了多种语言的绑定,包括Python、Java、C等,适用于多种操作系统,如Windows、Linux和macOS。
在Windows终端输入:
pip install opencv-python -i https://pypi.tuna.tsinghua.edu.cn/simple
这是用清华的镜像源,下载起来会快很多。 验证是否下载成功: 打开编辑器,查看安装OpenCV的版本
import cv2
print(cv2.__version__)
我的版本是4.10.0
本片文章,将用到四张图片,图中的4张图片。
import cv2
image = cv2.imread("opencv_logo.jpg")
print(image.shape)#打印维度
读取的图片数据会存储在image变量里,且为一个numpy数组类型。
打印结果
(250, 250, 3)
其中,(250,250)分别位图片像素的横行和纵列,最后一个3为,图片的3原色彩色通道。 打开画图板验证:
得到结果确实如此。 下面我们把读取到的图片打印到显示屏当中。
import cv2
image = cv2.imread("opencv_logo.jpg")
print(image.shape)
cv2.imshow("Image", image)
cv2.waitKey()
cv2.imshow('Image',image)
,用于在一个新窗口显示图像,第一个参数是窗口的名称,第二个参数是要显示的图像。
cv2.waitKey()
等待用户输入任意键,确保图像窗口不会立即关闭。
在数字图像处理中,彩色图像通常由三个颜色通道组成:红色(Red)、绿色(Green)和蓝色(Blue),这三个通道也被称为RGB通道。每个通道代表图像在该颜色上的强度分布。
在OpenCV中,图像是以BGR格式存储的,而不是常见的RGB格式。这意味着第一个通道是蓝色,第二个通道是绿色,第三个通道是红色。
对于OpenCV来说,存储一张彩色图片等同于存储3张灰度图片,它们被存储在OpenCV图像数据的第3个维度上,灰度范围是0-255
。当显示器需要渲染一张图片时,计算机会依次取出图像数据中的3张灰度图在把它们分别投影到显示器的蓝色、绿色和红色的led芯片上。
下面开始让显示屏上显示这3张灰度图以及原图:
import cv2
# 读取图像
image = cv2.imread("opencv_logo.jpg")
# 显示原始图像和各个通道
cv2.imshow("Original Image", image)
cv2.imshow("Blue Channel", image[:, :, 0])
cv2.imshow("Green Channel", image[:, :, 1])
cv2.imshow("Red Channel", image[:, :, 2])
# 等待按键,然后关闭所有窗口
cv2.waitKey(0)
cv2.destroyAllWindows()
OpenCV同时还提供一种彩色图像的灰度变换算法,可以把3个彩色通道的图像做平方和加权平均。
import cv2
# 读取图像
image = cv2.imread("opencv_logo.jpg")
# 显示原始图像和各个通道
cv2.imshow("Blue Channel", image[:, :, 0])
cv2.imshow("Green Channel", image[:, :, 1])
cv2.imshow("Red Channel", image[:, :, 2])
#显示灰度图像
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.imshow("Gray", gray)
# 等待按键,然后关闭所有窗口
cv2.waitKey(0)
cv2.destroyAllWindows()
可以看到gray是BGR3原色的平均,而且它也描述了图案的明暗分布,在计算机视觉邻域我们通常把这个变换后的图像gray
称为称为灰度图。
大量的图像算法都是基于灰度图来操作的
import cv2
image = cv2.imread("opencv_logo.jpg")
crop = image[10:170,40:200]
cv2.imshow("Crop", crop)
cv2.waitKey(0)
运行代码
可以看到,部分代码被提取出来了。 下面我将打开,Windows中的画图来进行讲解: 对于OpenCV索引的顺序是先横行后纵列
也就是说:索引10:170
对应的是第10横行到第170横行,对应的40:200
就是第40纵列到第200纵列。这个索引顺序可能与某些图像处理工具是不同的,比如加州理工大学基于MATLAB
的图像处理包为相反顺序。
本次代码需要引入numpy
工具包,实际上opencv的图像数据是numpy数组数据结构。
import cv2
import numpy as np
image = np.zeros([300,300,3],dtype=np.uint8)#利用numpy创建一个黑色画布
cv2.line(image,(100,200),(250,250),(255,0,0),2)
cv2.rectangle(image,(30,100),(60,150),(0,255,0),2)
cv2.circle(image,(150,100),20,(0,0,255),3)
cv2.putText(image,"Hello World",(100,50),0,1,(255,255,255),2,1)
cv2.imshow("Image",image)
cv2.waitKey(0)
下面我会开始介绍代码中出现的函数。
np.zeros
函数np.zeros([300,300,3],dtype=np.uint8)
创建一个300*300像素的黑色画布,并初始化所有像素值为0(也就是黑色)。
np.zeros
:NumPy库中的一个函数,用于创建一个指定形状和数据类型的全零数组。[300,300,3]
:这是数组的形状。这里表示一个300x300像素的图像,每个像素有3个通道(BGR颜色通道)。dtype=np.uint8
:指定数组的数据类型为无符号8位整数(unsigned 8-bit integer),这是图像处理中常用的数据类型,因为每个像素的BGR值通常在0到255之间。cv2.rectangle
函数cv2.rectangle(image,(30,100),(60,150),(0,255,0),2)
cv2.line
是 OpenCV 库中的一个函数,用于在图像上绘制一条直线。
image
:要在其上绘制直线的图像。(100, 200)
:直线的起点坐标。(250, 250)
:直线的终点坐标。(255, 0, 0)
:直线的颜色,这里是蓝色。2
:直线的粗细。cv2.rectangle
函数cv2.rectangle(image,(30,100),(60,150),(0,255,0),2)
cv2.rectangle
是 OpenCV 库中的一个函数,用于在图像上绘制一个矩形
image
:要在其上绘制矩形的图像。(30, 100)
:矩形左上角的坐标。(60, 150)
:矩形右下角的坐标。(0, 255, 0)
:矩形的颜色,这里是绿色。2
:矩形边框的粗细。cv2.circle
函数cv2.circle
是 OpenCV 库中的一个函数,用于在图像上绘制一个圆形。
cv2.circle(image,(150,100),20,(0,0,255),3)
image
:要在其上绘制圆形的图像。(150, 100)
:圆心的坐标。20
:圆的半径。(0, 0, 255)
:圆的颜色,这里是红色。3
:圆边框的粗细。cv2.putText
函数cv2.putText
是 OpenCV 库中的一个函数,用于在图像上绘制文本。
cv2.putText(image,"Hello World",(100,50),0,1,(255,255,255),2,1)
image
:要在其上绘制文本的图像。"Hello World"
:要绘制的文本内容。(100, 50)
:文本左下角的坐标。cv2.FONT_HERSHEY_SIMPLEX
:字体类型。1
:字体比例因子。(255, 255, 255)
:文本的颜色,这里是白色。2
:文本线条的粗细。cv2.LINE_AA
:文本线条的类型,抗锯齿线。本次代码会使用的到飞机素材。
该图片是一张噪点十分多的图片,下面我会用滤波器对其进行处理。
import cv2
image = cv2.imread('plane.jpg')
gauss = cv2.GaussianBlur(image, (5, 5), 0)#高斯滤波
median = cv2.medianBlur(image, 5)#均值滤波
cv2.imshow('Original', image)
cv2.imshow('Gaussian Blur', gauss)
cv2.imshow('Median Blur', median)
cv2.waitKey(0)
可以看到无论是高斯滤波器还是均值滤波器都使得图片的噪点噪点减少,不过也破坏了些图像细节。 实际的处理中,我们很难遇到噪点这么严重的图片,更多情况下,我们会遇到干净背景中的少数几个噪点,使用均值滤波把它消除。
cv2.GaussianBlur
函数cv2.GaussianBlur
是 OpenCV 库中的一个函数,用于对图像应用高斯模糊。
cv2.GaussianBlur(image, (5, 5), 0)
image
:输入图像。(5, 5)
:高斯核的大小,表示核的宽度和高度均为 5。0
:高斯核在 X 方向上的标准差,设为 0
表示自动计算。cv2.medianBlur
函数cv2.medianBlur
是 OpenCV 库中的一个函数,用于对图像应用中值滤波(Median Blur)。中值滤波是一种非线性滤波方法,它通过将每个像素的值替换为其邻域内像素值的中值来工作。这种方法对于去除椒盐噪声(salt-and-pepper noise)特别有效。
cv2.medianBlur(image, 5)
image
:输入图像。5
:滤波器的大小,表示邻域的大小为 5×55×5。import cv2
image = cv2.imread('opencv_logo.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
corners = cv2.goodFeaturesToTrack(gray, 500, 0.1, 10)
for corner in corners:
x,y = corner.ravel()#是 NumPy 数组的一个方法,用于将多维数组展平成一维数组。
cv2.circle(image,(int(x),int(y)),3,(255,0,255),-1)
cv2.imshow('Corners', image)
cv2.waitKey(0)
我们看到识别出来的特征都是图案的转角,转交是最简单的图像特征,提取转角的算法都是非常高效的。
cv2.goodFeaturesToTrack
函数cv2.goodFeaturesToTrack(gray, 500, 0.1, 10)
gray
:需要检测角点的灰度图像。500
:最大角点数,即函数最多会返回500个角点。0.1
:质量等级,表示角点的最小可接受质量。该值越小,检测到的角点越多,但质量可能较低。10
:最小距离,表示检测到的任意两个角点之间的最小像素距离。该值越大,检测到的角点越分散。
函数返回一个包含检测到的角点的数组,每个角点由其坐标(x, y)表示。模板匹配我们会用到扑克那张图片,来匹配扑克中的菱形。
import cv2
import numpy as np
image = cv2.imread("poker.jpg")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
template = gray[75:105, 235:265] #该区域刚好包含一个菱形
match = cv2.matchTemplate(gray, template, cv2.TM_CCOEFF_NORMED)
locations = np.where(match >= 0.9)#找出匹配系数大于0.9的匹配点
w, h = template.shape[0:2] #求出模板的长和宽,方便后续标记图片。
for p in zip(*locations[::-1]):
x1, y1 = p[0], p[1]
x2, y2 = x1 + w, y1 + h
cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.imshow("image", image)
cv2.waitKey()
我们这里使用的匹配算法是大小敏感的,如果想要把图片中的菱形都匹配出来,可以放到缩小图像多次来匹配。
cv2.matchTemplate
函数cv2.matchTemplate
函数在提供的代码中用于在灰度图像gray
中查找与模板template
最匹配的区域.
np.where
函数np.where(match >= 0.9)
是 NumPy 库中的一个函数调用,用于根据条件 match >= 0.9
查找数组 match
中满足条件的元素的索引
match
:这是一个二维数组,由 cv2.matchTemplate
函数返回,表示输入图像中每个位置与模板的匹配程度。match >= 0.9
:这是一个布尔数组,表示 match
中每个元素是否大于等于 0.9。np.where(match >= 0.9)
:这个函数调用返回一个元组,包含两个数组,分别表示满足条件的元素的行索引和列索引。我们前面学的特征点提取与图像匹配这些算法的背后都使用的图像梯度。 图像梯度就是图像的明暗变化,比如我们可以分别计算沿水平和垂直方向的明暗变化,再其这俩个变化的平方和就得到了梯度,它和地理上的梯度是一样的,只不过地面的高低起伏变成了图像的明暗变化。
import cv2
gray = cv2.imread("opencv_logo.jpg", cv2.IMREAD_GRAYSCALE)
laplacian = cv2.Laplacian(gray, cv2.CV_64F)#拉普拉斯算子
canny = cv2.Canny(gray, 100, 200)
cv2.imshow("gray", gray)
cv2.imshow("laplacian", laplacian)
cv2.imshow("canny", canny)
cv2.waitKey()
拉普拉斯算子给出了图像明暗变化的趋势,比如均一的背景区域变成了黑色,有明暗变化的部分,比如边缘就变成了白色,我们知道一个几何图形的变化往往有剧烈的明暗变化,所以梯度算法也常常用于检测边缘。
conny
边缘检测,在conny
算法中我们使用一个梯度区间来定义边缘,比如梯度区间100到200
![[Pasted image 20250127213341.png]]
import cv2
gray = cv2.imread("bookpage.jpg", cv2.IMREAD_GRAYSCALE)
ret, binary = cv2.threshold(gray, 10, 255, cv2.THRESH_BINARY)
binary_adaptive = cv2.adaptiveThreshold(
gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 115, 1)
ret1, binary_otsu = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
cv2.imshow("gray", gray)
cv2.imshow("binary", binary)
cv2.imshow("adaptive", binary_adaptive)
cv2.imshow("otsu", binary_otsu)
cv2.waitKey()
import cv2
import numpy as np
gray = cv2.imread("opencv_logo.jpg", cv2.IMREAD_GRAYSCALE)
_, binary = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV)
kernel = np.ones((5, 5), np.uint8)
erosion = cv2.erode(binary, kernel)
dilation = cv2.dilate(binary, kernel)
cv2.imshow("binary", binary)
cv2.imshow("erosion", erosion)
cv2.imshow("dilation", dilation)
cv2.waitKey()
import cv2
capture = cv2.VideoCapture(0)
while True:
ret, frame = capture.read()
cv2.imshow("camera", frame)
key = cv2.waitKey(1)
if key != -1:
break
capture.release()
从阈值算法往后我并没有详细介绍,所以本篇文章其实并不完整,我会在后续对该文章进行补充。 OpenCV是一个集轻量、高效、开源与一身的被使用最广泛的计算机视觉工具,非常值得程序员的学习。