下载完成后会得到一个 opencv-3.4.15-vc14_vc15.exe 文件,点击运行后会生成一个文件夹。
此文件夹为下一步工程创建使用,文件夹可移动、复制和重命名,这里命名如下:
附加包含目录:$(SolutionDir)\OpenCV3.4.15\opencv\build\include
附加lib库目录:$(SolutionDir)\OpenCV3.4.15\opencv\build\x64\vc15\lib
附加dll库目录:PATH=$(SolutionDir)\OpenCV3.4.15\opencv\build\x64\vc15\bin;%PATH%
Debug模式下的依赖项:opencv_world3415d.lib
Release模式下的依赖项:opencv_world3415.lib
至此配置完成,开启你的OpenCV之旅吧!
#include
#include
int main(int argc, char** argv)
{
std::cout << "OpenCV_Version: " << CV_VERSION << std::endl;
cv::waitKey(0);
return 0;
}
//示例一:以原格式读取并显示图像
#include
#include
using namespace cv;
int main(int argc, char** argv)
{
//读取图片
Mat img = imread("../images/liyifeng.jpg", cv::IMREAD_UNCHANGED);//以原图格式读取
if (img.empty()) {
std::cout << "加载图片失败" << std::endl;
}
else {
imshow("窗口1", img); //在窗口1中显示图片img(窗口大小不能调整)
imshow("窗口2", img); //在窗口2中显示图片img(窗口大小不能调整)
}
waitKey(0); //显示图片的窗口停顿
//waitKey(2000); //显示图片的窗口停顿2000ms
//destroyAllWindows(); //销毁所有的窗口
return 0;
}
//示例二:以灰度格式读取并显示图片
#include
#include
using namespace cv;
int main(int argc, char** argv)
{
Mat img = imread("../images/liyifeng.jpg", IMREAD_GRAYSCALE);//以灰度格式读取图片
if (img.empty()) {
std::cout << "加载图片失败" << std::endl;
}
else {
namedWindow("hello world", cv::WINDOW_FREERATIO); //创建一个名为hello world的窗口
imshow("hello world", img); //在hello world的窗口上显示图片img
}
waitKey(0); //显示图片的窗口停顿
//destroyAllWindows(); //销毁所有的窗口
return 0;
}
//WINDOW_FREERATIO:图片布满窗口,窗口大小也可以拉伸调整
#include
#include
using namespace cv;
int main(int argc, char** argv)
{
//读取图片
Mat img_source = imread("../images/liyifeng.jpg", IMREAD_UNCHANGED);//以原图格式读取
if (img_source.empty()) {
std::cout << "加载图片失败" << std::endl;
}
else {
Mat img_hsv, img_gray;
cvtColor(img_source, img_hsv, COLOR_BGR2HSV); //img_source转HSV格式,结果存到img_hsv中
cvtColor(img_source, img_gray, COLOR_BGR2GRAY); //img_source转灰度格式,结果存到img_gray中
imshow("img_hsv - HSV空间", img_hsv);
imshow("img_gray - 灰度空间", img_gray);
}
waitKey(0); //显示图片的窗口停顿
//destroyAllWindows(); //销毁所有的窗口
return 0;
}
//COLOR_BGR2GRAY = 6 : BGR到灰度
//COLOR_GRAY2BGR = 8 : 灰度到BGR
//COLOR_BGR2HSV = 40 : BGR到HSV
//COLOR_HSV2BGR = 54 : HSV到BGR
#include
#include
using namespace cv;
int main(int argc, char** argv)
{
//读取图片
Mat img_source = imread("../images/liyifeng.jpg", IMREAD_UNCHANGED);//以原图格式读取
if (img_source.empty()) {
std::cout << "加载图片失败" << std::endl;
}
else {
if (imwrite("../images/liyifeng_save.jpg", img_source)){ //注意.jpg是有损压缩,.png是无损压缩
std::cout << "图片保存成功" << std::endl;
}
else {
std::cout << "图片保存失败" << std::endl;
}
}
waitKey(0); //显示图片的窗口停顿
//destroyAllWindows(); //销毁所有的窗口
return 0;
}
// 示例一:创建
#include
#include
using namespace cv;
int main(int argc, char** argv)
{
Mat img = Mat::zeros(Size(4, 4), CV_8UC3); //CV_8UC3:8位、unsigned char、三通道
namedWindow("imgx 窗口", WINDOW_FREERATIO); //创建一个名为"imgx 窗口"的窗口
//显示一
std::cout << "显示一:显示图像的参数" << "\r\n";
std::cout << img << "\r\n"; //打印矩阵
std::cout << "dims:" << img.dims << "\r\n"; //打印维度
std::cout << "width:" << img.cols << "\r\n"; //打印宽度
std::cout << "height:" << img.rows << "\r\n"; //打印高度
std::cout << "size:" << img.size << "\r\n"; //打印尺寸
std::cout << "total:" << img.total() << "\r\n"; //打印像素总数
std::cout << "elemSize" << img.elemSize() << "\r\n"; //矩阵每个像素所占字节数(一个像素三个数组元素)
std::cout << "channel:" << img.channels() << "\r\n"; //矩阵每个像素所用通道数(一个像素三个数组元素)
std::cout << "type:" << cv::typeToString(img.type()) << "\r\n"; //矩阵每个像素的数据类型
std::cout << "depth:" << cv::depthToString(img.depth()) << "\r\n"; //矩阵每个像素的通道的数据类型
//显示二
std::cout << "显示二:图像的赋值操作" << "\r\n";
Mat imgx;
img.copyTo(imgx); //把img拷贝一份给imgx
imgx = Scalar(255, 0, 0); //imgx的每个像素点各通道都赋值成(0,0,255)
std::cout << imgx << "\r\n"; //打印矩阵
imshow("imgx 窗口", imgx);
//显示三(C++知识)
//Mat imgy = imgx; //浅拷贝
//imgx.copyTo(imgy); //条件深拷贝
//Mat imgy = imgx.clone(); //完全深拷贝
waitKey(0); //显示图片的窗口停顿
//destroyAllWindows(); //销毁所有的窗口
return 0;
}
//示例: 遍历与读写
#include
#include
using namespace cv;
int main(int argc, char** argv)
{
Mat image = Mat::zeros(Size(2, 2), CV_8UC3); //CV_8UC3:8位三通道
int width = image.cols; //列
int height = image.rows; //行
int channel = image.channels(); //通道数
//一、修改图像各点像素值
switch (channel) {
case 1: {
image.at(0, 0) = 0x00; image.at(0, 1) = 0xFF;
image.at(1, 0) = 0xFF; image.at(1, 1) = 0x00;
break;
}
case 3: { //彩色图像
image.at(0, 0) = { 0,0,255 }; image.at(0, 1) = { 0,255,0 }; // 00 01
image.at(1, 0) = { 255,0,0 }; image.at(1, 1) = { 0,0,0 }; // 10 11
//image.at(row, col)[0] = 0; //写、(单独写一个像素B通道)
//image.at(row, col)[1] = 255; //写、(单独写一个像素G通道)
//image.at(row, col)[2] = 0; //写、(单独写一个像素R通道)
break;
}
default: {break; }
}
//二、遍历图像读取像素值
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
switch (channel) {
case 1: {//灰度图像(row=行,col=列)
int pv = image.at(row, col); //读
std::cout << "row,col=" << pv << "\r\n";
break;
}
case 3: { //彩色图像
Vec3b bgr = image.at(row, col);//读
std::cout << "row,col=" << bgr << "\r\n";
break;
}
default: {break; }
}
}
}
cv::namedWindow("imgx 窗口", WINDOW_FREERATIO); //创建一个名为"imgx 窗口1"的窗口
cv::imshow("imgx 窗口", image);
cv::waitKey(0); //显示图片的窗口停顿
//cv::destroyAllWindows(); //销毁所有的窗口
return 0;
}
opencv3.4.1.15之后的版本,在某些识别算法上商业化了,所以建议使用3.4.1.15版本的,但是由于conda和pip中已经搜不到opencv3.4.1.15的版本了,这里提供pip的离线安装包下载:
文件下载地址:https://download.csdn.net/download/BaoTTing/85133357
离线安装命令:pip install folder:xxx.whl
(说明:资源已经限制在5积分下载,不会动态调整)
//示例、几何绘制
# 这是一个示例 Python 脚本。
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
img = np.zeros((512, 512, 3), np.uint8)
cv.line(img, (0, 0), (511, 511), (255, 0, 0), 5)
cv.circle(img, (256, 256), 60, (0, 0, 255), -1)
cv.rectangle(img, (100, 100), (400, 400), (0, 255, 0), 5)
cv.putText(img, "hello", (100, 150), cv.FONT_HERSHEY_COMPLEX, 5, (255, 255, 255), 3)
plt.imshow(img[:, :, ::-1])
plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
# 示例、像素修改和格式获取
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
img = cv.imread("ant.jpg", cv.IMREAD_UNCHANGED)
px = img[50, 50] # 获取某个像素点的值
print("px = ", px) # 打印px的值
blue = img[50, 50, 0] # 仅获取蓝色通道的强度值
print("blue = ", blue) # 打印blue的值
print("img.shape = ", img.shape) # 打印像素尺寸
print("img.size = ", img.size) # 打印有
print("img.dtype = ", img.dtype) # 打印通道的数据类型
img[100, 100] = [255, 255, 255] # 修改某个位置的像素值
img[100, 101] = [255, 255, 255] # 修改某个位置的像素值
img[100, 102] = [255, 255, 255] # 修改某个位置的像素值
img[100, 103] = [255, 255, 255] # 修改某个位置的像素值
img[100, 104] = [255, 255, 255] # 修改某个位置的像素值
plt.imshow(img[:, :, ::-1]) # BGR转RGB
plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
# 示例、边界填充
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
# 1、读取图像
img = cv.imread("ant.jpg", cv.IMREAD_UNCHANGED)
# 2、边界填充尺寸
top_size, bottom_size, left_size, right_size = (50, 50, 50, 50)
# 3、边界填充
replicate = cv.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, borderType=cv.BORDER_REPLICATE) # 边缘复制
reflect1 = cv.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, borderType=cv.BORDER_REFLECT) # 反射法1
reflect2 = cv.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, borderType=cv.BORDER_REFLECT_101) # 反射法2
wrap = cv.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, borderType=cv.BORDER_WRAP) # 外包装法
constant = cv.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, borderType=cv.BORDER_CONSTANT, value=0) # 常量填充
# 5、使用matplot进行图像显示
fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(10, 8), dpi=100)
axes[0, 0].imshow(img[:, :, ::-1])
axes[0, 0].set_title("原图")
axes[0, 1].imshow(replicate[:, :, ::-1])
axes[0, 1].set_title("边缘复制")
axes[0, 2].imshow(reflect1[:, :, ::-1])
axes[0, 2].set_title("反射法1")
axes[1, 0].imshow(reflect2[:, :, ::-1])
axes[1, 0].set_title("反射法2")
axes[1, 1].imshow(wrap[:, :, ::-1])
axes[1, 1].set_title("外包装法")
axes[1, 2].imshow(constant[:, :, ::-1])
axes[1, 2].set_title("常量填充")
plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
# 示例、图像的分割与合并
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
img = cv.imread("ant.jpg", cv.IMREAD_UNCHANGED)
b, g, r = cv.split(img) # 通道拆分
img = cv.merge((b, g, r)) # 通道合并
plt.subplot(1, 2, 1)
plt.imshow(b)
img = img[:, :, ::-1] # BGR转RGB
plt.subplot(1, 2, 2)
plt.imshow(img) # 对图像进行处理
plt.show() # 对图像进行显示
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
# 示例、图像格式的转换、色彩空间
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
input_img = cv.imread("ant.jpg", cv.IMREAD_UNCHANGED)
img_hsv = cv.cvtColor(input_img, cv.COLOR_BGR2HSV)
img_gray = cv.cvtColor(input_img, cv.COLOR_BGR2GRAY)
plt.subplot(1, 2, 1)
plt.imshow(img_hsv)
plt.subplot(1, 2, 2)
plt.imshow(img_gray, cmap=plt.cm.gray)
plt.show() # 对图像进行显示
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
# 示例、图像的加法
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
# 1、数值加法
# numpy的加法是饱和取模运算、超过边界取模值
# OpenCV的加法是饱和取边界操作、超过边界取边界值
x = np.uint8([250])
y = np.uint8([10])
print("numpy add=", x+y) # numpy-> 250 + 10 = 260%256 = 4
print("OpenCV add=", cv.add(x, y)) # OpenCV-> 250 + 10 = 255
# 对比两种加法,可见OpenCV的加法更常用(减法也是cv比较常用)
# 2、图像加法
img1 = cv.imread("view.jpg")
img2 = cv.imread("rain.jpg")
npadd_img = img1 + img2
cvadd_img = cv.add(img1, img2)
# 3、图像显示
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(10, 8), dpi=100)
axes[0].imshow(npadd_img[:, :, ::-1])
axes[0].set_title("np add")
axes[1].imshow(cvadd_img[:, :, ::-1])
axes[1].set_title("cv add")
plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
# 示例、图像的混合
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
# 图像的混合也是加法,不同的是两幅图像的权重不同,给人一种混合或者透明的感觉。
# 即 g(x) = (1-a)*f0(x)+a*f1(x),改变a的值可以实现非常炫酷的混合。
# cv2.addWeight()可以按公式: dst = a*img1+b*img2+c进行混合(取c=0即可)。
img1 = cv.imread("view.jpg")
img2 = cv.imread("rain.jpg")
img_blend_1 = cv.addWeighted(img1, 0.7, img2, 0.3, 0)
img_blend_2 = cv.addWeighted(img1, 0.3, img2, 0.7, 0)
# 图像显示
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(10, 8), dpi=100)
axes[0].imshow(img_blend_1[:, :, ::-1])
axes[0].set_title("a=0.7,b=0.3,c=0")
axes[1].imshow(img_blend_2[:, :, ::-1])
axes[1].set_title("a=0.3,b=0.7,c=0")
plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
# 示例、图像缩放
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 图像缩放:对图像的大小进行调整,即图像的放大或者缩小
# 函数:cv2.resize(src,desize,fx=0,fy=0,interpolation=cv2.INTER_LINEAR)
# src:输入图像
# dsize:绝对尺寸,直接指定调整后图像大小
# fx,fy:相对尺寸,将desize设置为None,然后将fx,fy设置为比例因子即可
# interpolation:插值方法
# cv2.INTER_LINEAR:双线性插值法
# cv2.INTER_NEAREST:最近邻插值
# cv2.INTER_AREA:像素区域重采样
# cv2.INTER_CUBIC:双三次插值法
# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
img_src = cv.imread("kids.jpg")
rows, cols = img_src.shape[:2]
print("img_src_size:", img_src.shape)
# 绝对尺寸
img_abs = cv.resize(img_src, (2 * cols, 2 * rows), interpolation=cv.INTER_CUBIC)
print("img_abs_size:", img_abs.shape)
# 相对尺寸
img_com = cv.resize(img_src, None, fx=0.5, fy=0.5)
print("img_com_size:", img_com.shape)
# 1、使用cv进行图像显示
cv.imshow("img_src", img_src)
cv.imshow("img_abs", img_abs)
cv.imshow("img_com", img_com)
cv.waitKey(0)
# 2、使用matplot进行图像显示
# fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(10, 8), dpi=100)
# axes[0].imshow(img_src[:, :, ::-1])
# axes[0].set_title("原始图片(放大)")
# axes[1].imshow(img_abs[:, :, ::-1])
# axes[1].set_title("绝对尺寸(放大)")
# axes[2].imshow(img_com[:, :, ::-1])
# axes[2].set_title("相对尺寸(缩小)")
# plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
# 示例、图像平移
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 图像平移:将图像按照指定的方向和距离,移动相应的位置
# 函数:cv2.wrapAffine(img, M, dsize)
# img:输入图像
# M:2*3的移动矩阵,对于(x, y)处的像素点, 要把它移动到(x+tx,y+ty处),使用如下矩阵
# M = [ 1 0 tx]
# [ 0 1 ty]
# 注意:将M设置为np.float32类型的Numpy数组
# desize: 输出图像的大小,它是(width,height)的形式,即(列数,行数)
# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
img_src = cv.imread("kids.jpg")
rows, cols = img_src.shape[:2]
print("img_src_size:", img_src.shape)
# 图像平移
M = np.float32([[1, 0, 100], [0, 1, 50]])
img_trans1 = cv.warpAffine(img_src, M, (cols, rows))
img_trans2 = cv.warpAffine(img_src, M, (2*cols, 2*rows))
print("img_trans1_size:", img_trans1.shape)
print("img_trans2_size:", img_trans1.shape)
# 1、使用cv进行图像显示
cv.imshow("img_src", img_src)
cv.imshow("img_trans1", img_trans1)
cv.imshow("img_trans2", img_trans2)
cv.waitKey(0)
# 2、使用matplot进行图像显示
# fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(10, 8), dpi=100)
# axes[0].imshow(img_src[:, :, ::-1])
# axes[0].set_title("原始图片")
# axes[1].imshow(img_trans1[:, :, ::-1])
# axes[1].set_title("平移图片1")
# axes[2].imshow(img_trans2[:, :, ::-1])
# axes[2].set_title("平移图片2")
# plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
# 示例、图像旋转
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# API函数1:cv2.getRotationMatrix2D(center,angle,scale) 获取旋转矩阵
# center:旋转中心
# angle:旋转角度
# scale:缩放比例
# API函数2:cv2.wrapAffine() 用法见示例八
# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
img_src = cv.imread("kids.jpg")
rows, cols = img_src.shape[:2]
print("img_src_size:", img_src.shape)
# 图像旋转
M = cv.getRotationMatrix2D((cols/2, rows/2), 45, 1.0) # 获取旋转矩阵
img_rotate = cv.warpAffine(img_src, M, (cols, rows))
print("img_rotate_size:", img_rotate.shape)
# 使用cv进行图像显示
cv.imshow("img_src", img_src)
cv.imshow("img_rotate", img_rotate)
cv.waitKey(0)
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
# 示例、仿射变换
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 仿射变换涉及图像的形状位置角度的变化,是深度学习与预处理中常用到的功能,仿射变换主要是对图像的缩放、旋转、翻转、平移等操作的组合
# 在OpenCV中,仿射变换是一个2*3的矩阵 M=[A B] <其实就是建立一种映射关系A->B>
# API函数1:cv2.getAffineTransform(src, dst) 获取仿射变换矩阵
# src:原坐标
# dst:映射坐标
# API函数2:cv2.wrapAffine() 用法见示例八
# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
# 1、图像读取
img_src = cv.imread("kids.jpg")
print("img_src_size:", img_src.shape)
# 2、仿射变换
rows, cols = img_src.shape[:2]
# 2.1、创建变换矩阵
pst1 = np.float32([[50, 50], [200, 50], [50, 200]])
pst2 = np.float32([[100, 100], [200, 50], [100, 250]])
M = cv.getAffineTransform(pst1, pst2)
# 2.2、进行图像的仿射变换
img_dst = cv.warpAffine(img_src, M, (cols, rows))
print("img_rotate_size:", img_dst.shape)
# 1、使用cv进行图像显示
cv.imshow("img_src", img_src)
cv.imshow("img_rotate", img_dst)
cv.waitKey(0)
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
# 示例、透射变换
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 透射变换是视角变化的结果,是指利用透视中心、像点、目标点、三点共线的条件,按照透视变换定律使得透
# 视面(承影面)绕迹线(透视轴)旋转某一角度,破坏原有的投影光束,仍能保持承影面上投影几何不变的变换。
# API函数1:cv2.getPerspectiveTransform(pts1, pts2)
# API函数2:cv2.wrapPerspective() 用法见示例八
# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
# 1、图像读取
img_src = cv.imread("kids.jpg")
print("img_src_size:", img_src.shape)
# 2、投射变换
rows, cols = img_src.shape[:2]
# 2.1、创建变换矩阵
pst1 = np.float32([[56, 65], [368, 52], [28, 387], [389, 390]])
pst2 = np.float32([[100, 145], [300, 100], [80, 290], [310, 300]])
M = cv.getPerspectiveTransform(pst1, pst2)
# 2.2、进行图像的仿射变换
img_dst = cv.warpPerspective(img_src, M, (cols, rows))
print("img_rotate_size:", img_dst.shape)
# 1、使用cv进行图像显示
cv.imshow("img_src", img_src)
cv.imshow("img_rotate", img_dst)
cv.waitKey(0)
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
# 示例、图像金字塔
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 图像金字塔是图像多尺寸表达,类似OpenGL中的mip纹理,以多种分辨率来解释图像的有效表达
# 图像金字塔用于机器视觉和图像压缩,一幅图像金字塔是一系列以金字塔形状排列的分辨率逐渐降低
# 且来源于同一张原始图的图像集合,其通过梯次向下采样获得,直到达到某个终止条件才停止采样
# 金字塔的底部是待处理的图像高分辨率表示,而顶部是低分辨率的近似,图像越高,分辨率越低
# API函数1、cv.pyrUp(img) #对图像进行上采样,分辨率增大
# API函数2、cv.pyrDown(img) #对图像进行下采样,分辨率降低
# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
# 1、图像读取
img_src = cv.imread("kids.jpg")
print("img_src_size:", img_src.shape)
# 2、图像金字塔
rows, cols = img_src.shape[:2]
# 2.1、图片上采样
img_Up = cv.pyrUp(img_src)
print("img_Up_size:", img_Up.shape)
# 2.2、图像下采样
img_Down = cv.pyrDown(img_src)
print("img_Down_size:", img_Down.shape)
# 1、使用cv进行图像显示
cv.imshow("img_Up", img_Up)
cv.imshow("img_src", img_src)
cv.imshow("img_Down", img_Down)
cv.waitKey(0)
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
# 示例、腐蚀与膨胀
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 图像邻接:每个像素周围有8个邻接像素,常见的邻接关系有4邻接、8邻接、D邻接
# 图像的连通:1、两个像素是否相邻。2、两个像素的像素值是否满足特定的相似准则
# 4连通:对于具有值V的像素p和q,如果q在p的4邻接中,则称这两个像素为4连通
# 8连通:对于具有值V的像素p和q,如果q在p的8邻接中,则称这两个像素为8连通
# m连通:对于具有值V的像素p和q,如果q在p的4邻接或D邻接中,且p的4邻接和q的4邻接不相交(交集中没有值为V的像素),则称这两个像素为m连通
# 腐蚀:局部最小值,(消除物体的边界点,使目标缩小,可用于消除小于结构元素的噪声点)
# 膨胀:局部最大值,(将与物体接触的所有背景点合并到物体中,使目标增大,可填补目标中的孔洞)
# API函数1:cv2.erode(img, kernel, iterations)
# API函数2:cv2.dilate(img, kernel, iterations)
# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
# 1、图像读取
img_src = cv.imread("letter.png")
print("img_src_size:", img_src.shape)
# 2、创建核结构
kernel = np.ones((5, 5), np.uint8)
# 3、图像腐蚀
img_erode = cv.erode(img_src, kernel)
# 4、图片膨胀
img_dilate = cv.dilate(img_src, kernel)
# 5、使用matplot进行图像显示
fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(10, 8), dpi=100)
axes[0].imshow(img_src[:, :, ::-1])
axes[0].set_title("img_src")
axes[1].imshow(img_erode[:, :, ::-1])
axes[1].set_title("img_erode")
axes[2].imshow(img_dilate[:, :, ::-1])
axes[2].set_title("img_dilate")
plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
# 示例、开、闭操作
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 开操作是先腐蚀后膨胀,作用是分离物体,消除小区域,消除噪点,去除小的干扰块。
# 闭操作是先膨胀后腐蚀,作用是消除闭合物体里面的孔洞,可以填充闭合区域
# API函数:cv2.morphologyEx(img, op, kernel)
# op=cv2.MORPH_OPEN:开操作
# op=cv2.MORPH_CLOSE:闭操作
# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
# 1、图像读取
img_src1 = cv.imread("letteropen.png")
img_src2 = cv.imread("letterclose.png")
print("img_src1_size:", img_src1.shape)
print("img_src2_size:", img_src2.shape)
# 2、创建核结构
kernel = np.ones((10, 10), np.uint8)
# 3、图像开操作
img_src1_open = cv.morphologyEx(img_src1, cv.MORPH_OPEN, kernel)
# 4、图片闭操作
img_src2_close = cv.morphologyEx(img_src2, cv.MORPH_CLOSE, kernel)
# 5、使用matplot进行图像显示
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(10, 8), dpi=100)
axes[0, 0].imshow(img_src1[:, :, ::-1])
axes[0, 0].set_title("原图1")
axes[0, 1].imshow(img_src1_open[:, :, ::-1])
axes[0, 1].set_title("原图1开操作")
axes[1, 0].imshow(img_src2[:, :, ::-1])
axes[1, 0].set_title("原图2")
axes[1, 1].imshow(img_src2_close[:, :, ::-1])
axes[1, 1].set_title("原图2闭操作")
plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
# 示例、礼帽与黑帽操作
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 礼帽操作:原图像与开运算的结果差(dst=src-open(src,element))
# 开运算的结果是放大了裂缝或局部低亮度区域,因此,从原图中减去开运算的图,得到的效果突出了比原图轮廓周围区域更明亮的区域,且这一操作和选择核的大小相关。
# 礼貌运算用来分离比临近点亮一些的斑块,当一幅图像具有大幅的背景的时候,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。
# 黑帽操作:闭运算与原图像的结果差(dst=close(src,element)-src)
# 黑帽运算后的效果图突出了比原图轮廓周围的区域更暗的区域,且这一操作和选择的核的大小相关。黑帽运算用来分离比邻近点暗一些的斑块。
# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
# 1、读取图像
img_src1 = cv.imread("letteropen.png")
img_src2 = cv.imread("letterclose.png")
print("img_src1_size:", img_src1.shape)
print("img_src2_size:", img_src2.shape)
# 2、创建核结构
kernel = np.ones((10, 10), np.uint8)
# 3、图像开操作
img_src1_tophat = cv.morphologyEx(img_src1, cv.MORPH_TOPHAT, kernel)
# 4、图片闭操作
img_src2_blackhat = cv.morphologyEx(img_src2, cv.MORPH_BLACKHAT, kernel)
# 5、使用matplot进行图像显示
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(10, 8), dpi=100)
axes[0, 0].imshow(img_src1[:, :, ::-1])
axes[0, 0].set_title("原图1")
axes[0, 1].imshow(img_src1_tophat[:, :, ::-1])
axes[0, 1].set_title("原图1礼帽运算")
axes[1, 0].imshow(img_src2[:, :, ::-1])
axes[1, 0].set_title("原图2")
axes[1, 1].imshow(img_src2_blackhat[:, :, ::-1])
axes[1, 1].set_title("原图2黑帽操作")
plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
# 示例、图像的平滑
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 均值滤波:
# API:cv2.blur(src, ksize, anchor, borderType)
# 高斯滤波:
# API:cv2.GaussianBlur(src,ksize,sigmaX,sigmay,borderType)
# 中值滤波:
# API:cv2.medianBlur(src, ksize)
# 双边滤波:
# API:cv.bilateralFilter(src, d, sigmaColor,sigmaSpace, dst, borderType)
if __name__ == '__main__':
# 1、读取图像
img_sp_src = cv.imread("dogsp.jpeg") # 带椒盐噪声原图
img_ga_src = cv.imread("dogGauss.jpeg") # 带高斯噪声原图
print("img_sp_size:", img_sp_src.shape)
print("img_ga_size:", img_ga_src.shape)
# 2、均值滤波
img_sp_dst1 = cv.blur(img_sp_src, (5, 5)) # 原图1的均值滤波
img_sp_dst2 = cv.blur(img_ga_src, (5, 5)) # 原图2的均值滤波
img_ga_dst = cv.GaussianBlur(img_ga_src, (3, 3), 1) # 高斯噪声(适合高斯噪声)
img_me_dst = cv.medianBlur(img_sp_src, 5) # 中值滤波(适合椒盐噪声)
# 5、使用matplot进行图像显示
fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(10, 8), dpi=100)
axes[0, 0].imshow(img_sp_src[:, :, ::-1])
axes[0, 0].set_title("带椒盐噪声原图1")
axes[0, 1].imshow(img_sp_dst1[:, :, ::-1])
axes[0, 1].set_title("原图1的均值滤波")
axes[0, 2].imshow(img_me_dst[:, :, ::-1])
axes[0, 2].set_title("原图1的中值滤波")
axes[1, 0].imshow(img_ga_src[:, :, ::-1])
axes[1, 0].set_title("带高斯噪声原图2")
axes[1, 1].imshow(img_sp_dst2[:, :, ::-1])
axes[1, 1].set_title("原图2均值滤波")
axes[1, 2].imshow(img_ga_dst[:, :, ::-1])
axes[1, 2].set_title("原图2高斯滤波")
plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
# 示例、直方图均衡化
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 使用OpenCV中的方法统计直方图,并使用matplotlib将其绘制出来
# 掩膜的作用是提取感兴趣的区域(蒙版->掩膜)
# 直方图均衡化能提升对比度,用于处理曝光过度或曝光不足的图片
# 统计直方图的API:cv2.calcHist(images,channels,mask,histSize,ranges[,hist[,accumulate]])
if __name__ == '__main__':
# 1、读取图像
img_src = cv.imread("cat.jpeg", cv.IMREAD_GRAYSCALE) # 以灰度的格式读取图片
# 2、统计完整直方图(不带掩膜)
img_histr = cv.calcHist([img_src], [0], None, [256], [0, 256])
# 3、统计带掩膜的直方图
# 3.1、创建蒙版
mask = np.zeros(img_src.shape[:2], np.uint8)
mask[400:650, 200:500] = 1
# 3.2、创建掩膜
masked_img = cv.bitwise_and(img_src, img_src, mask=mask) # 蒙版与原图相与
# 3.3、原图带掩膜的直方图
mask_histr = cv.calcHist([img_src], [0], mask, [256], [0, 256])
# 4、对原图进行直方图均衡化
img_dst = cv.equalizeHist(img_src)
# 5、使用matplot进行图像显示
fig, axes = plt.subplots(nrows=3, ncols=2, figsize=(10, 6), dpi=100)
axes[0, 0].imshow(img_src, cmap=plt.cm.gray)
axes[0, 0].set_title("原图")
axes[0, 1].plot(img_histr)
axes[0, 1].set_title("原图直方图(不带掩膜)")
axes[0, 1].grid() # 添加网格
axes[1, 0].imshow(mask, cmap=plt.cm.gray)
axes[1, 0].set_title("蒙版数据")
axes[1, 1].imshow(masked_img, cmap=plt.cm.gray)
axes[1, 1].set_title("掩膜数据")
axes[2, 0].plot(mask_histr)
axes[2, 0].set_title("带掩膜的直方图")
axes[2, 0].grid() # 添加网格
axes[2, 1].imshow(img_dst, cmap=plt.cm.gray)
axes[2, 1].set_title("直方图均衡化结果")
plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
# 示例、自适应均衡化
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 直方图均衡化是对图像整体的均衡,改变全局对比度,虽说能增强背景细节,但某些区域对比度好的区域在直方图均衡化后效果会变差
# cv.createCLAHE(clipLimit, tileGridSize)
# clipLimit: 对比度限制,默认是40
# tileGridSize: 分块的大小,默认为8*88∗8
if __name__ == '__main__':
# 1、读取图像
img_src = cv.imread("cat.jpeg", cv.IMREAD_GRAYSCALE) # 以灰度的格式读取图片
# 2、统计原图直方图
img_src_histr = cv.calcHist([img_src], [0], None, [256], [0, 256])
# 3、创建一个自适应均衡化的对象
clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
# 4、自适应均衡化
img_dst = clahe.apply(img_src)
# 5、统计均衡化后的直方图
img_dst_histr = cv.calcHist([img_dst], [0], None, [256], [0, 256])
# 6、使用matplot进行图像显示
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(10, 6), dpi=100)
axes[0, 0].imshow(img_src, cmap=plt.cm.gray)
axes[0, 0].set_title("原图")
axes[0, 1].plot(img_src_histr)
axes[0, 1].set_title("原图直方图")
axes[0, 1].grid() # 添加网格
axes[1, 0].imshow(img_dst, cmap=plt.cm.gray)
axes[1, 0].set_title("自适应均衡化")
axes[1, 1].plot(img_dst_histr)
axes[1, 1].set_title("均衡化后的直方图")
axes[1, 1].grid() # 添加网格
plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
# 示例、边缘检测之Sobel
# Sobel算子:用于边缘检测,简单效率高,虽然不如Canny检测准确,但实际很多场合,Sobel算子是首选(Sobel算子基于一阶导数)
# [ -1 +0 +1 ] [ -1 -2 -1 ]
# Gx = [ -2 +0 +2 ] Gy = [ +0 +0 +0 ] Gx用于检测水平变化,Gy用于检测垂直变化
# [ -1 +0 +1 ] [ +1 +2 +1 ]
# Scharr算子:当内核大小为3时,以上Sobel可能产生比较明显误差,我们使用Scharr算子替代来解决这个问题(Scharr仅适用于内核大小为3)
# [ -3 +0 +3 ] [ -3 -10 -3 ]
# Gx = [ -10 +0 +10 ] Gy = [ +0 +0 +0 ] Gx用于检测水平变化,Gy用于检测垂直变化
# [ -3 +0 +3 ] [ +3 +10 +3 ]
# API函数 cv2.Sobel(src, ddepth, dx, dy, dst, ksize, scale, delta, borderType)
# (src:传入的图像)(ddepth:图像的深度)(dx和dy:指求导的阶数,0表示这个方向上没有求导,取值为0、1)
# (ksize:是Sobel算子的大小,即卷积核的大小,必须为奇数1、3、5、7,默认为3.注意:如果ksize=-1,就演变成为3x3的Scharr算子)
# (scale:缩放导数的比例常数,默认情况为没有伸缩系数)(borderType:图像边界的模式,默认值为cv2.BORDER_DEFAULT)
# ->Sobel函数求完导数后会有负值,还有会大于255的值。而原图像是uint8,即8位无符号数,所以Sobel建立的图像位数不够,会有截断。
# ->因此要使用16位有符号的数据类型,即cv2.CV_16S。处理完图像后,再使用cv2.convertScaleAbs()函数将其转回原来的uint8格式,否则图像无法显示。
# ->Sobel算子是在两个方向计算的,最后还需要用cv2.addWeighted()函数将其组合起来
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 主函数
if __name__ == '__main__':
# 1、读取图像
img_src = cv.imread("horse.jpg", cv.IMREAD_GRAYSCALE) # 以灰度的格式读取图片
# 2、计算Sobel卷积结果
sobel_x = cv.Sobel(img_src, cv.CV_16S, 1, 0) # Scharr: cv.Sobel(img_src, cv.CV_16S, 1, 0, ksize=-1)
sobel_y = cv.Sobel(img_src, cv.CV_16S, 0, 1) # Scharr: cv.Sobel(img_src, cv.CV_16S, 0, 1, ksize=-1)
# 3、将数据进行转换
scaleAbs_x = cv.convertScaleAbs(sobel_x)
scaleAbs_y = cv.convertScaleAbs(sobel_y)
# 4、结果合成
result = cv.addWeighted(scaleAbs_x, 0.5, scaleAbs_y, 0.5, 0)
# 5、使用matplot进行图像显示
plt.subplot(1, 2, 1), plt.imshow(img_src, cmap=plt.cm.gray), plt.title("原图")
plt.subplot(1, 2, 2), plt.imshow(result, cmap=plt.cm.gray), plt.title("Sobel滤波后结果")
plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
# 示例、边缘检测之Laplacian(拉普拉斯)
# Laplacian算子:Laplacian是利用二阶导数来检测边缘
# [ +0 +1 +0 ]
# kernel = [ +1 -4 +1 ]
# [ +0 +1 +0 ]
# API函数:cv2.Laplacian(src, ddepth[, dst[, ksize[, scale[, delta[, borderType]]]]])
# Src:需要处理的图像.
# Ddepth:图像的深度,-1表示采用的是原图像相同的深度,目标图像的深度必须大于等于原图像的深度.
# ksize:算子的大小,即卷积核的大小,必须为1,3,5,7.
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 主函数
if __name__ == '__main__':
# 1、读取图像
img_src = cv.imread("horse.jpg", cv.IMREAD_GRAYSCALE) # 以灰度的格式读取图片
# 2、laplacian
lap_dst = cv.Laplacian(img_src, cv.CV_16S)
img_dst = cv.convertScaleAbs(lap_dst)
# 3、使用matplot进行图像显示
plt.subplot(1, 2, 1), plt.imshow(img_src, cmap=plt.cm.gray), plt.title("原图")
plt.subplot(1, 2, 2), plt.imshow(img_dst, cmap=plt.cm.gray), plt.title("Laplacian滤波后结果")
plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
# 示例、边缘检测Canny
# Canny边缘检测算法是由4步构成,分别介绍如下:
# 第一步:噪声去除(由于边缘检测很容易受到噪声的影响,所以首先使用$5*5$高斯滤波器去除噪声)
# 第二步:计算图像梯度(对平滑后的图像使用Sobel算子计算水平方向和竖直方向的一阶导数(Gx和Gy),梯度方向分为四类:垂直,水平,和两个对角线方向)
# 第三步:非极大值抑制(在获得梯度的方向和大小之后,对整幅图像进行扫描,去除那些非边界上的点。对每一个像素进行检查,看这个点的梯度是不是周围具有相同梯度方向的点中最大的)
# 第四步:滞后阈值(现在要确定真正的边界。我们设置两个阈值:minVal和maxVal。当图像的灰度梯度高于maxVal时被认为是真的边界,
# 低于minVal的边界会被抛弃。如果介于两者之间的话,就要看这个点是否与某个被确定为真正的边界点相连,如果是就认为它也是边界点,如果不是就抛弃)
# API函数:cv2.Canny(image, threshold1, threshold2)
# image:灰度图。
# threshold1: minval,较小的阈值将间断的边缘连接起来。
# threshold2: maxval,较大的阈值检测图像中明显的边缘。
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 主函数
if __name__ == '__main__':
# 1、读取图像
img_src = cv.imread("horse.jpg", cv.IMREAD_GRAYSCALE) # 以灰度的格式读取图片
# 2、Canny检测
img_dst = cv.Canny(img_src, 0, 100)
# 3、使用matplot进行图像显示
plt.subplot(1, 2, 1), plt.imshow(img_src, cmap=plt.cm.gray), plt.title("原图")
plt.subplot(1, 2, 2), plt.imshow(img_dst, cmap=plt.cm.gray), plt.title("Canny后的结果")
plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
常见算子的比较与总结:
所谓的模板匹配,就是在给定的图片中查找和模板最相似的区域,该算法的输入包括模板和图片,整个任务的思路就是按照滑窗的思路不断的移动模板图片,计算其与图像中对应区域的匹配度,最终将匹配度最高的区域选择为最终的结果。
实现流程:
准备两幅图像:
1.原图像(I):在这幅图中,找到与模板相匹配的区域
2.模板(T):与原图像进行比对的图像块
滑动模板图像和原图像进行比对:
将模板块每次移动一个像素 (从左往右,从上往下),在每一个位置,都计算与模板图像的相似程度。
API:
res = cv.matchTemplate(img,template,method)
参数:
完成匹配后,使用cv.minMaxLoc()方法查找最大值所在的位置即可。如果使用平方差作为比较方法,则最小值位置是最佳匹配位置。
<示例一:从图片中找出人物的位置>
在该案例中,载入要搜索的图像和模板,图像如下所示:
模板如下所示:
用matchTemplate实现模板匹配,使用minMaxLoc定位最匹配的区域并用矩形标注最匹配的区域。
# 示例、模板匹配(1)
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 主函数
if __name__ == '__main__':
# 1、读取图像
img_src = cv.imread("wulin.jpeg", cv.IMREAD_UNCHANGED) # 读取原图
img_tmp = cv.imread("wulin2.jpeg", cv.IMREAD_UNCHANGED) # 读取模板
h, w = img_tmp.shape[:2]
# 2 模板匹配
# 2.1 模板匹配
res = cv.matchTemplate(img_src, img_tmp, cv.TM_CCORR)
# 2.2 返回图像中最匹配的位置,确定左上角的坐标,并将匹配位置绘制在图像上
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(res)
# 使用平方差时最小值为最佳匹配位置
# top_left = min_loc
top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
cv.rectangle(img_src, top_left, bottom_right, (0, 255, 0), 2)
# 3、使用matplot进行图像显示
plt.subplot(1, 2, 1), plt.imshow(res, cmap=plt.cm.gray), plt.title("模板匹配结果") # 灰度图不需要交换通道,因为只有一个通道
plt.subplot(1, 2, 2), plt.imshow(img_src[:, :, ::-1]), plt.title("结果")
plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
<示例二:从图片中找出数字>
# 文件一:myutils
import cv2
# 函数1:
def sort_contours(cnts, method="left-to-right"):
reverse = False
i = 0
if method == "right-to-left" or method == "bottom-to-top":
reverse = True
if method == "top-to-bottom" or method == "bottom-to-top":
i = 1
boundingBoxes = [cv2.boundingRect(c) for c in cnts] # 用一个最小的矩形,把找到的形状包起来x,y,h,w
(cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes), key=lambda b: b[1][i], reverse=reverse))
return cnts, boundingBoxes
# 函数2:
def resize(image, width=None, height=None, inter=cv2.INTER_AREA):
dim = None
(h, w) = image.shape[:2]
if width is None and height is None:
return image
if width is None:
r = height / float(h)
dim = (int(w * r), height)
else:
r = width / float(w)
dim = (width, int(h * r))
resized = cv2.resize(image, dim, interpolation=inter)
return resized
# 文件二:main.py
# 程序运行方法:python main.py -i credit_card_01.png -t ocr_a_reference.png
from imutils import contours
import numpy as np
import argparse
import cv2
import myutils
# 设置参数
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True, help="path to input image")
ap.add_argument("-t", "--template", required=True, help="path to template OCR-A image")
args = vars(ap.parse_args())
# 指定信用卡类型
FIRST_NUMBER = {
"3": "American Express",
"4": "Visa",
"5": "MasterCard",
"6": "Discover Card"
}
# 绘图展示
def cv_show(name, img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
return
# 读取一个模板图像
img = cv2.imread(args["template"])
# cv_show('img', img)
# 灰度图
ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# cv_show('ref', ref)
# 二值图像
ref = cv2.threshold(ref, 10, 255, cv2.THRESH_BINARY_INV)[1] # 返回第二个参数
# cv_show('ref', ref)
# 计算轮廓
# cv2.findContours()函数接受的参数为二值图,即黑白的(不是灰度图),cv2.RETR_EXTERNAL只检测外轮廓,cv2.CHAIN_APPROX_SIMPLE只保留终点坐标
# 返回的list中每个元素都是图像中的一个轮廓
ref_, refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img, refCnts, -1, (0, 0, 255), 3)
cv_show('img', img)
print(np.array(refCnts).shape)
refCnts = myutils.sort_contours(refCnts, method="left-to-right")[0] # 排序,从左到右,从上到下
digits = {}
# 遍历每一个轮廓
for (i, c) in enumerate(refCnts):
# 计算外接矩形并且resize成合适大小
(x, y, w, h) = cv2.boundingRect(c)
roi = ref[y:y + h, x:x + w]
roi = cv2.resize(roi, (57, 88))
# 每一个数字对应每一个模板
digits[i] = roi
# 初始化卷积核
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# 读取输入图像,预处理
image = cv2.imread(args["image"])
cv_show('image', image)
image = myutils.resize(image, width=300)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv_show('gray', gray)
# 礼帽操作,突出更明亮的区域
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)
cv_show('tophat', tophat)
#
gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1) # ksize=-1相当于用3*3的
gradX = np.absolute(gradX)
(minVal, maxVal) = (np.min(gradX), np.max(gradX))
gradX = (255 * ((gradX - minVal) / (maxVal - minVal)))
gradX = gradX.astype("uint8")
print(np.array(gradX).shape)
cv_show('gradX', gradX)
# 通过闭操作(先膨胀,再腐蚀)将数字连在一起
gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel)
cv_show('gradX', gradX)
# THRESH_OTSU会自动寻找合适的阈值,适合双峰,需把阈值参数设置为0
thresh = cv2.threshold(gradX, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv_show('thresh', thresh)
# 再来一个闭操作
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel) # 再来一个闭操作
cv_show('thresh', thresh)
# 计算轮廓
thresh_, threshCnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = threshCnts
cur_img = image.copy()
cv2.drawContours(cur_img, cnts, -1, (0, 0, 255), 3)
cv_show('img', cur_img)
locs = []
# 遍历轮廓
for (i, c) in enumerate(cnts):
# 计算矩形
(x, y, w, h) = cv2.boundingRect(c)
ar = w / float(h)
# 选择合适的区域,根据实际任务来,这里的基本都是四个数字一组
if ar > 2.5 and ar < 4.0:
if (w > 40 and w < 55) and (h > 10 and h < 20):
# 符合的留下来
locs.append((x, y, w, h))
# 将符合的轮廓从左到右排序
locs = sorted(locs, key=lambda x: x[0])
output = []
# 遍历每一个轮廓中的数字
for (i, (gX, gY, gW, gH)) in enumerate(locs):
# initialize the list of group digits
groupOutput = []
# 根据坐标提取每一个组
group = gray[gY - 5:gY + gH + 5, gX - 5:gX + gW + 5]
cv_show('group', group)
# 预处理
group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv_show('group', group)
# 计算每一组的轮廓
group_, digitCnts, hierarchy = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
digitCnts = contours.sort_contours(digitCnts, method="left-to-right")[0]
# 计算每一组中的每一个数值
for c in digitCnts:
# 找到当前数值的轮廓,resize成合适的的大小
(x, y, w, h) = cv2.boundingRect(c)
roi = group[y:y + h, x:x + w]
roi = cv2.resize(roi, (57, 88))
cv_show('roi', roi)
# 计算匹配得分
scores = []
# 在模板中计算每一个得分
for (digit, digitROI) in digits.items():
# 模板匹配
result = cv2.matchTemplate(roi, digitROI, cv2.TM_CCOEFF)
(_, score, _, _) = cv2.minMaxLoc(result)
scores.append(score)
# 得到最合适的数字
groupOutput.append(str(np.argmax(scores)))
# 画出来
cv2.rectangle(image, (gX - 5, gY - 5), (gX + gW + 5, gY + gH + 5), (0, 0, 255), 1)
cv2.putText(image, "".join(groupOutput), (gX, gY - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)
# 得到结果
output.extend(groupOutput)
# 打印结果
print("Credit Card Type: {}".format(FIRST_NUMBER[output[0]]))
print("Credit Card #: {}".format("".join(output)))
cv2.imshow("Image", image)
cv2.waitKey(0)
霍夫变换常用来提取图像中的直线和圆等几何形状,如下图所示:
<霍夫线检测>
在OpenCV中做霍夫线检测是使用的API是:
cv.HoughLines(img, rho, theta, threshold)
参数:
img: 检测的图像,要求是二值化的图像,所以在调用霍夫变换之前首先要进行二值化,或者进行Canny边缘检测
rho、theta: \rhoρ 和\thetaθ的精确度
threshold: 阈值,只有累加器中的值高于该阈值时才被认为是直线。
霍夫线检测的整个流程如下图所示,这是在stackflow上一个关于霍夫线变换的解释:
霍夫线检测示例:检测下述图像中的直线:
# 示例、霍夫线检测(1)
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 主函数
if __name__ == '__main__':
# 1、读取图像
img_src = cv.imread("rili.jpg", cv.IMREAD_UNCHANGED)
img_gray = cv.cvtColor(img_src, cv.COLOR_BGR2GRAY)
img_edges = cv.Canny(img_gray, 50, 150)
# 2、霍夫直线变换
lines = cv.HoughLines(img_edges, 0.8, np.pi/180, 160)
for line in lines:
rho, theta = line[0]
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)
cv.line(img_src, (x1, y1), (x2, y2), (0, 255, 0))
# 3、使用matplot进行图像显示
plt.subplot(2, 2, 1), plt.imshow(img_gray, cmap=plt.cm.gray), plt.title("原图的灰度图")
plt.subplot(2, 2, 2), plt.imshow(img_edges, cmap=plt.cm.gray), plt.title("灰度图的Canny检测")
#plt.subplot(2, 2, 3), plt.imshow(lines), plt.title("灰") #显示霍夫空间
plt.subplot(2, 2, 3), plt.imshow(img_src[:, :, ::-1]), plt.title("霍夫线检测")
plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
<霍夫圆检测>
原理
圆的表示式是:(x-a)^2+(y-b)^2=r,其中a和b表示圆心坐标,r表示圆半径,因此标准的霍夫圆检测就是在这三个参数组成的三维空间累加器上进行圆形检测,此时效率就会很低,所以OpenCV中使用霍夫梯度法进行圆形的检测。
霍夫梯度法将霍夫圆检测范围两个阶段,第一阶段检测圆心,第二阶段利用圆心推导出圆半径。
圆心检测的原理:圆心是圆周法线的交汇处,设置一个阈值,在某点的相交的直线的条数大于这个阈值就认为该交汇点为圆心。
圆半径确定原理:圆心到圆周上的距离(半径)是相同的,确定一个阈值,只要相同距离的数量大于该阈值,就认为该距离是该圆心的半径。
原则上霍夫变换可以检测任何形状,但复杂的形状需要的参数就多,霍夫空间的维数就多,因此在程序实现上所需的内存空间以及运行效率上都不利于把标准霍夫变换应用于实际复杂图形的检测中。霍夫梯度法是霍夫变换的改进,它的目的是减小霍夫空间的维度,提高效率。
API
在OpenCV中检测图像中的圆环使用的是API是:
circles = cv.HoughCircles(image, method, dp, minDist, param1=100, param2=100, minRadius=0,maxRadius=0 )
参数:
image:输入图像,应输入灰度图像
method:使用霍夫变换圆检测的算法,它的参数是CV_HOUGH_GRADIENT
dp:霍夫空间的分辨率,dp=1时表示霍夫空间与输入图像空间的大小一致,dp=2时霍夫空间是输入图像空间的一半,以此类推
minDist为圆心之间的最小距离,如果检测到的两个圆心之间距离小于该值,则认为它们是同一个圆心
param1:边缘检测时使用Canny算子的高阈值,低阈值是高阈值的一半。
param2:检测圆心和确定半径时所共有的阈值
minRadius和maxRadius为所检测到的圆半径的最小值和最大值
返回:circles:输出圆向量,包括三个浮点型的元素——圆心横坐标,圆心纵坐标和圆半径
实现:由于霍夫圆检测对噪声比较敏感,所以首先对图像进行中值滤波
# 示例、霍夫圆检测(2)
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 主函数
if __name__ == '__main__':
# 1、读取图像
img_src = cv.imread("star.jpeg", cv.IMREAD_UNCHANGED)
img_gray = cv.cvtColor(img_src, cv.COLOR_BGR2GRAY)
# 2、进行中值模糊,去噪点
img_me = cv.medianBlur(img_gray, 7)
# 3、霍夫圆检测
circles = cv.HoughCircles(img_me, cv.HOUGH_GRADIENT, 1, 200, param1=100, param2=30, minRadius=0, maxRadius=100)
# 4、将检测结果绘制在图像上
for i in circles[0, :]: # 遍历矩阵每一行的数据
# 绘制圆形
cv.circle(img_src, (i[0], i[1]), i[2], (0, 255, 0), 2)
# 绘制圆心
cv.circle(img_src, (i[0], i[1]), 2, (0, 0, 255), 3)
# 3、使用matplot进行图像显示
plt.subplot(1, 2, 1), plt.imshow(img_me, cmap=plt.cm.gray), plt.title("灰度图的中值")
plt.subplot(1, 2, 2), plt.imshow(img_src[:, :, ::-1]), plt.title("霍夫圆检测")
plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
傅里叶变换到频域:
高频:变化剧烈的灰度分量,例如边界。
低频:变化缓慢的灰度分量,例如一片大海。
频域滤波:
低通滤波器:只保留低频,会使图像变模糊。
高通滤波器:只保留高频,会使得图像的细节增强。
OpenCV中的API:
cv2.dft() : 傅里叶正变换,输入图像需要先转换为np.float32格式。
cv2.idft() : 傅里叶反变换,返回的结果是复数形式,需要转换图像格式。
<示例一:图像的傅里叶变换>
# 示例、傅里叶变换(1)
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 主函数
if __name__ == '__main__':
# 1 图像读取
img_src = cv.imread('lena.jpg', cv.IMREAD_GRAYSCALE) # 以灰度格式读取原图
img_float32 = np.float32(img_src) # 转换成float32的格式
# 2、傅里叶变换
dft = cv.dft(img_float32, flags=cv.DFT_COMPLEX_OUTPUT) # 正向傅里叶变换
dft_shift = np.fft.fftshift(dft) # 移相
# 3、得到灰度图能表示的格式
img_magnitude = 20*np.log(cv.magnitude(dft_shift[:, :, 0], dft_shift[:, :, 0])) # magnitude 计算矩阵维度的平方根
# 4、显示变换结果(中间是低频,越往外频率越高)
plt.subplot(121), plt.imshow(img_src, cmap="gray") # 源图像
plt.subplot(122), plt.imshow(img_magnitude, cmap="gray") # 傅里叶变换结果
plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
<示例二:低通滤波器的简单实现>
# 示例、低通滤波器的简单实现(2)
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 主函数
if __name__ == '__main__':
# 1 图像读取
img_src = cv.imread('lena.jpg', cv.IMREAD_GRAYSCALE) # 以灰度格式读取原图
rows, cols = img_src.shape # 图像尺寸
crow, ccol = int(rows/2), int(cols/2) # 中心位置
img_float32 = np.float32(img_src) # 转换成float32的格式
# 2、离散傅里叶变换
dft = cv.dft(img_float32, flags=cv.DFT_COMPLEX_OUTPUT) # 正向傅里叶变换
# 3、shift变换
dft_shift = np.fft.fftshift(dft) # 将零频点移到频谱的中间(shift后,中间为低频部分)
# 4、创建掩膜,保留中心的掩膜
mask = np.zeros((rows, cols, 2), np.uint8) # 因为输出是复数,所以这里深度为2
mask[crow-30:crow+30, ccol-30:ccol+30] = 1 # 掩膜中心的一个方块置为1
# 5、低通滤波
fshift = dft_shift*mask # 保留低频部分、
# 6、逆shift变换
f_ishift = np.fft.ifftshift(fshift) # 之前进行了shift,这里要先逆shift,再傅里叶反变换
# 7、逆离散傅里叶变换
img_back = cv.idft(f_ishift)
img_back = cv.magnitude(img_back[:, :, 0], img_back[:, :, 1]) # magnitude:取模
# 8、显示变换结果(中间是低频,越往外频率越高)
plt.subplot(121), plt.imshow(img_src, cmap="gray") # 源图像
plt.subplot(122), plt.imshow(img_back, cmap="gray") # 低频滤波后
plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
<示例二:高通滤波器的简单实现>
# 示例、高通滤波器的简单实现(3)
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 主函数
if __name__ == '__main__':
# 1 图像读取
img_src = cv.imread('lena.jpg', cv.IMREAD_GRAYSCALE) # 以灰度格式读取原图
rows, cols = img_src.shape # 图像尺寸
crow, ccol = int(rows/2), int(cols/2) # 中心位置
img_float32 = np.float32(img_src) # 转换成float32的格式
# 2、离散傅里叶变换
dft = cv.dft(img_float32, flags=cv.DFT_COMPLEX_OUTPUT) # 正向傅里叶变换
# 3、shift变换
dft_shift = np.fft.fftshift(dft) # 将零频点移到频谱的中间(shift后,中间为低频部分)
# 4、创建掩膜,保留中心的掩膜
mask = np.ones((rows, cols, 2), np.uint8) # 因为输出是复数,所以这里深度为2
mask[crow-30:crow+30, ccol-30:ccol+30] = 0 # 掩膜中心的一个方块置为0
# 5、低通滤波
fshift = dft_shift*mask # 保留低频部分、
# 6、逆shift变换
f_ishift = np.fft.ifftshift(fshift) # 之前进行了shift,这里要先逆shift,再傅里叶反变换
# 7、逆离散傅里叶变换
img_back = cv.idft(f_ishift)
img_back = cv.magnitude(img_back[:, :, 0], img_back[:, :, 1]) # magnitude:取模
# 8、显示变换结果(中间是低频,越往外频率越高)
plt.subplot(121), plt.imshow(img_src, cmap="gray") # 源图像
plt.subplot(122), plt.imshow(img_back, cmap="gray") # 低频滤波后
plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
原理:
Harris角点检测的思想是通过图像的局部的小窗口观察图像,角点的特征是窗口沿任意方向移动都会导致图像灰度的明显变化,如下图所示:
将上述思想转换为数学形式,即将局部窗口向各个方向移动(u,v)(u,v)并计算所有灰度差异的总和,表达式如下:
其中I(x,y)是局部窗口的图像灰度,I(x+u,y+v)是平移后的图像灰度,w(x,y)是窗口函数,该可以是矩形窗口,也可以是对每一个像素赋予不同权重的高斯窗口,如下所示:
角点检测中使E(u,v)的值最大。利用一阶泰勒展开有:
其中Ix和Iy 是沿x和y方向的导数,可用sobel算子计算。推导如下:
M矩阵决定了E(u,v)的取值,下面我们利用M来求角点,M是Ix和Iy的二次项函数,可以表示成椭圆的形状,椭圆的长短半轴由M的特征值λ1和λ2决定,方向由特征矢量决定,如下图所示:
椭圆函数特征值与图像中的角点、直线(边缘)和平面之间的关系如下图所示。
共可分为三种情况:
Harris给出的角点计算方法并不需要计算具体的特征值,而是计算一个角点响应值R来判断角点。R的计算公式为:
式中,detM为矩阵M的行列式;traceM为矩阵M的迹;α为常数,取值范围为0.04~0.06。事实上,特征是隐含在detM和traceM中,因为:
那我们怎么判断角点呢?如下图所示:
实现:
在OpenCV中实现Hariis检测使用的API是:
dst=cv.cornerHarris(src, blockSize, ksize, k)
参数:
img:数据类型为 float32 的输入图像。
blockSize:角点检测中要考虑的邻域大小。
ksize:sobel求导使用的核大小
k :角点检测方程中的自由参数,取值参数为 [0.04,0.06].
示例:
# 示例25、Harries
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 主函数
if __name__ == '__main__':
# 1、读取图像,并转换成灰度图像
img = cv.imread('chessboard.jpg')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 2、角点检测
# 2.1 输入图像必须是 float32
gray = np.float32(gray)
# 2.2 最后一个参数在 0.04 到 0.05 之间
dst = cv.cornerHarris(gray, 2, 3, 0.04)
# 3、设置阈值,将角点绘制出来,阈值根据图像进行选择
img[dst > 0.001 * dst.max()] = [0, 0, 255]
# 4、图像显示
plt.figure(figsize=(10, 8), dpi=100)
plt.imshow(img[:, :, ::-1]), plt.title('Harris角点检测')
plt.xticks([]), plt.yticks([])
plt.show()
# x、使用matplot进行图像显示
#plt.subplot(1, 2, 1), plt.imshow(xx, cmap=plt.cm.gray), plt.title("灰度图的中值")
#plt.subplot(1, 2, 2), plt.imshow(xx[:, :, ::-1]), plt.title("霍夫圆检测")
#plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
原理:
Shi-Tomasi算法是对Harris角点检测算法的改进,一般会比Harris算法得到更好的角点。Harris 算法的角点响应函数是将矩阵 M 的行列式值与 M 的迹相减,利用差值判断是否为角点。后来Shi 和Tomasi 提出改进的方法是,若矩阵M的两个特征值中较小的一个大于阈值,则认为他是角点,即:
R=min(λ1,λ2)
如下图所示:
从这幅图中,可以看出来只有当 λ1 和 λ 2 都大于最小值时,才被认为是角点。
实现:
在OpenCV中实现Shi-Tomasi角点检测使用API:
corners = cv2.goodFeaturesToTrack ( image, maxcorners, qualityLevel, minDistance )
参数:
返回:
示例:
# 示例26、Shi-Tomas角点检测
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 主函数
if __name__ == '__main__':
# 1 读取图像
img = cv.imread('tv.jpg')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 2 角点检测
corners = cv.goodFeaturesToTrack(gray, 1000, 0.01, 10)
# 3 绘制角点
for i in corners:
x, y = i.ravel()
cv.circle(img, (x, y), 2, (0, 0, 255), -1)
# 4 图像展示
plt.figure(figsize=(10, 8), dpi=100)
plt.imshow(img[:, :, ::-1]), plt.title('shi-tomasi角点检测')
plt.xticks([]), plt.yticks([])
plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
<原理>
前面两节我们介绍了Harris和Shi-Tomasi角点检测算法,这两种算法具有旋转不变性,但不具有尺度不变性,以下图为例,在左侧小图中可以检测到角点,但是图像被放大后,在使用同样的窗口,就检测不到角点了。
所以,下面我们来介绍一种计算机视觉的算法,尺度不变特征转换即SIFT (Scale-invariant feature transform)。它用来侦测与描述影像中的局部性特征,它在空间尺度中寻找极值点,并提取出其位置、尺度、旋转不变量,此算法由 David Lowe在1999年所发表,2004年完善总结。应用范围包含物体辨识、机器人地图感知与导航、影像缝合、3D模型建立、手势辨识、影像追踪和动作比对等领域。
SIFT算法的实质是在不同的尺度空间上查找关键点(特征点),并计算出关键点的方向。SIFT所查找到的关键点是一些十分突出,不会因光照,仿射变换和噪音等因素而变化的点,如角点、边缘点、暗区的亮点及亮区的暗点等。
<基本流程>
Lowe将SIFT算法分解为如下四步:
我们就沿着Lowe的步骤,对SIFT算法的实现过程进行介绍:
<1、尺度空间极值检测>
在不同的尺度空间是不能使用相同的窗口检测极值点,对小的关键点使用小的窗口,对大的关键点使用大的窗口,为了达到上述目的,我们使用尺度空间滤波器。
高斯核是唯一可以产生多尺度空间的核函数。-《Scale-space theory: A basic tool for analysing structures at different scales》。
一个图像的尺度空间L(x,y,σ),定义为原始图像I(x,y)与一个可变尺度的2维高斯函数G(x,y,σ)卷积运算 ,即:
L(x,y,σ)=G(x,y,σ)∗I(x,y)
其中:
σ是尺度空间因子,它决定了图像的模糊的程度。在大尺度下(\sigmaσ值大)表现的是图像的概貌信息,在小尺度下(\sigmaσ值小)表现的是图像的细节信息。
在计算高斯函数的离散近似时,在大概3σ距离之外的像素都可以看作不起作用,这些像素的计算也就可以忽略。所以,在实际应用中,只计算(6σ+1)*(6σ+1)的高斯卷积核就可以保证相关像素影响。
下面我们构建图像的高斯金字塔,它采用高斯函数对图像进行模糊以及降采样处理得到的,高斯金字塔构建过程中,首先将图像扩大一倍,在扩大的图像的基础之上构建高斯金字塔,然后对该尺寸下图像进行高斯模糊,几幅模糊之后的图像集合构成了一个Octave,然后对该Octave下选择一幅图像进行下采样,长和宽分别缩短一倍,图像面积变为原来四分之一。这幅图像就是下一个Octave的初始图像,在初始图像的基础上完成属于这个Octave的高斯模糊处理,以此类推完成整个算法所需要的所有八度构建,这样这个高斯金字塔就构建出来了,整个流程如下图所示:
利用LoG(高斯拉普拉斯方法),即图像的二阶导数,可以在不同的尺度下检测图像的关键点信息,从而确定图像的特征点。但LoG的计算量大,效率低。所以我们通过两个相邻高斯尺度空间的图像的相减,得到DoG(高斯差分)来近似LoG。
为了计算DoG我们构建高斯差分金字塔,该金字塔是在上述的高斯金字塔的基础上构建而成的,建立过程是:在高斯金字塔中每个Octave中相邻两层相减就构成了高斯差分金字塔。如下图所示:
高斯差分金字塔的第1组第1层是由高斯金字塔的第1组第2层减第1组第1层得到的。以此类推,逐组逐层生成每一个差分图像,所有差分图像构成差分金字塔。概括为DOG金字塔的第o组第l层图像是有高斯金字塔的第o组第l+1层减第o组第l层得到的。后续Sift特征点的提取都是在DOG金字塔上进行的
在 DoG 搞定之后,就可以在不同的尺度空间中搜索局部最大值了。对于图像中的一个像素点而言,它需要与自己周围的 8 邻域,以及尺度空间中上下两层中的相邻的 18(2x9)个点相比。如果是局部最大值,它就可能是一个关键点。基本上来说关键点是图像在相应尺度空间中的最好代表。如下图所示:
搜索过程从每组的第二层开始,以第二层为当前层,对第二层的DoG图像中的每个点取一个3×3的立方体,立方体上下层为第一层与第三层。这样,搜索得到的极值点既有位置坐标(DoG的图像坐标),又有空间尺度坐标(层坐标)。当第二层搜索完成后,再以第三层作为当前层,其过程与第二层的搜索类似。当S=3时,每组里面要搜索3层,所以在DOG中就有S+2层,在初使构建的金字塔中每组有S+3层。
<2、关键点定位>
由于DoG对噪声和边缘比较敏感,因此在上面高斯差分金字塔中检测到的局部极值点需经过进一步的检验才能精确定位为特征点。
使用尺度空间的泰勒级数展开来获得极值的准确位置, 如果极值点的 灰度值小于阈值(一般为0.03或0.04)就会被忽略掉。 在 OpenCV 中这种阈值被称为 contrastThreshold。
DoG 算法对边界非常敏感, 所以我们必须要把边界去除。 Harris 算法除了可以用于角点检测之外还可以用于检测边界。从 Harris 角点检测的算法中,当一个特征值远远大于另外一个特征值时检测到的是边界。那在DoG算法中欠佳的关键点在平行边缘的方向有较大的主曲率,而在垂直于边缘的方向有较小的曲率,两者的比值如果高于某个阈值(在OpenCV中叫做边界阈值),就认为该关键点为边界,将被忽略,一般将该阈值设置为10。
将低对比度和边界的关键点去除,得到的就是我们感兴趣的关键点。
<3、关键点确定方向>
经过上述两个步骤,图像的关键点就完全找到了,这些关键点具有尺度不变性。为了实现旋转不变性,还需要为每个关键点分配一个方向角度,也就是根据检测到的关键点所在高斯尺度图像的邻域结构中求得一个方向基准。
对于任一关键点,我们采集其所在高斯金字塔图像以r为半径的区域内所有像素的梯度特征(幅值和幅角),半径r为:
r = 3×1.5σ
其中σ是关键点所在octave的图像的尺度,可以得到对应的尺度图像。
梯度的幅值和方向的计算公式为:
邻域像素梯度的计算结果如下图所示:
完成关键点梯度计算后,使用直方图统计关键点邻域内像素的梯度幅值和方向。具体做法是,将360°分为36柱,每10°为一柱,然后在以r为半径的区域内,将梯度方向在某一个柱内的像素找出来,然后将他们的幅值相加在一起作为柱的高度。因为在r为半径的区域内像素的梯度幅值对中心像素的贡献是不同的,因此还需要对幅值进行加权处理,采用高斯加权,方差为1.5σ。如下图所示,为简化图中只画了8个方向的直方图。
每个特征点必须分配一个主方向,还需要一个或多个辅方向,增加辅方向的目的是为了增强图像匹配的鲁棒性。辅方向的定义是,当一个柱体的高度大于主方向柱体高度的80%时,则该柱体所代表的的方向就是给特征点的辅方向。
直方图的峰值,即最高的柱代表的方向是特征点邻域范围内图像梯度的主方向,但该柱体代表的角度是一个范围,所以我们还要对离散的直方图进行插值拟合,以得到更精确的方向角度值。利用抛物线对离散的直方图进行拟合,如下图所示:
获得图像关键点主方向后,每个关键点有三个信息(x,y,σ,θ):位置、尺度、方向。由此我们可以确定一个SIFT特征区域。通常使用一个带箭头的圆或直接使用箭头表示SIFT区域的三个值:中心表示特征点位置,半径表示关键点尺度,箭头表示方向。如下图所示:
<4、关键点描述>
通过以上步骤,每个关键点就被分配了位置,尺度和方向信息。接下来我们为每个关键点建立一个描述符,该描述符既具有可区分性,又具有对某些变量的不变性,如光照,视角等。而且描述符不仅仅包含关键点,也包括关键点周围对其有贡献的的像素点。主要思路就是通过将关键点周围图像区域分块,计算块内的梯度直方图,生成具有特征向量,对图像信息进行抽象。
描述符与特征点所在的尺度有关,所以我们在关键点所在的高斯尺度图像上生成对应的描述符。以特征点为中心,将其附近邻域划分为d∗d个子区域(一般取d=4),每个子区域都是一个正方形,边长为3σ,考虑到实际计算时,需进行三次线性插值,所以特征点邻域的为3σ(d+1)∗3σ(d+1)的范围,如下图所示:
为了保证特征点的旋转不变性,以特征点为中心,将坐标轴旋转为关键点的主方向,如下图所示:
计算子区域内的像素的梯度,并按照σ=0.5d进行高斯加权,然后插值计算得到每个种子点的八个方向的梯度,插值方法如下图所示:
每个种子点的梯度都是由覆盖其的4个子区域插值而得的。如图中的红色点,落在第0行和第1行之间,对这两行都有贡献。对第0行第3列种子点的贡献因子为dr,对第1行第3列的贡献因子为1-dr,同理,对邻近两列的贡献因子为dc和1-dc,对邻近两个方向的贡献因子为do和1-do。则最终累加在每个方向上的梯度大小为:
其中k,m,n为0或为1。 如上统计4*4*8=1284∗4∗8=128个梯度信息即为该关键点的特征向量,按照特征点的对每个关键点的特征向量进行排序,就得到了SIFT特征描述向量。
<5、总结>
SIFT在图像的不变特征提取方面拥有无与伦比的优势,但并不完美,仍然存在实时性不高,有时特征点较少,对边缘光滑的目标无法准确提取特征点等缺陷,自SIFT算法问世以来,人们就一直对其进行优化和改进,其中最著名的就是SURF算法。
使用 SIFT 算法进行关键点检测和描述的执行速度比较慢, 需要速度更快的算法。 2006 年 Bay提出了 SURF 算法,是SIFT算法的增强版,它的计算量小,运算速度快,提取的特征与SIFT几乎相同,将其与SIFT算法对比如下
<6、实现>
在OpenCV中利用SIFT检测关键点的流程如下所示:
第一步、实例化sift :
sift = cv.xfeatures2d.SIFT_create()
第二步、利用sift.detectAndCompute()检测关键点并计算
kp,des = sift.detectAndCompute(gray,None)
参数:
返回:
第三步、将关键点检测结果绘制在图像上
cv.drawKeypoints(image, keypoints, outputimage, color, flags)
参数:
SURF算法的应用与上述流程是一致,这里就不在赘述。
示例:利用SIFT算法在中央电视台的图片上检测关键点,并将其绘制出来:
# 示例、SIFT/SURF算法
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 主函数
if __name__ == '__main__':
# 1 读取图像
img = cv.imread('tv.jpg')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 2 sift关键点检测
# 2.1 实例化sift对象
sift = cv.xfeatures2d.SIFT_create()
# 2.2 关键点检测:kp关键点信息包括方向,尺度,位置信息,des是关键点的描述符
kp, des = sift.detectAndCompute(gray, None)
# 2.3 在图像上绘制关键点的检测结果
cv.drawKeypoints(img, kp, img, flags=cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
# 3 图像显示
plt.figure(figsize=(8, 6), dpi=100)
plt.imshow(img[:, :, ::-1]), plt.title('sift检测')
plt.xticks([]), plt.yticks([])
plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
我们前面已经介绍过几个特征检测器,它们的效果都很好,特别是SIFT和SURF算法,但是从实时处理的角度来看,效率还是太低了。为了解决这个问题,Edward Rosten和Tom Drummond在2006年提出了FAST算法,并在2010年对其进行了修正。
<原理>
(全称Features from accelerated segment test)是一种用于角点检测的算法,该算法的原理是取图像中检测点,以该点为圆心的周围邻域内像素点判断检测点是否为角点,通俗的讲就是若一个像素周围有一定数量的像素与该点像素值不同,则认为其为角点
<基本流程>
1. 在图像中选取一个像素点 p,来判断它是不是关键点。Ip等于像素点 p的灰度值。
2. 以r为半径画圆,覆盖p点周围的M个像素,通常情况下,设置 r=3,则 M=16,如下图所示:
3. 设置一个阈值t,如果在这 16 个像素点中存在 n 个连续像素点的灰度值都高于Ip + t,或者低于Ip - t,那么像素点 p 就被认为是一个角点。如上图中的虚线所示,n 一般取值为 12。
4. 由于在检测特征点时是需要对图像中所有的像素点进行检测,然而图像中的绝大多数点都不是特征点,如果对每个像素点都进行上述的检测过程,那显然会浪费许多时间,因此采用一种进行**非特征点判别的方法:首先对候选点的周围每个 90 度的点:1,9,5,13 进行测试(先测试 1 和 19, 如果它们符合阈值要求再测试 5 和 13)。如果 p 是角点,那么这四个点中至少有 3 个要符合阈值要求,否则直接剔除。对保留下来的点再继续进行测试(是否有 12 的点符合阈值要求)。
虽然这个检测器的效率很高,但它有以下几条缺点:
- 获得的候选点比较多
- 特征点的选取不是最优的,因为它的效果取决与要解决的问题和角点的分布情况。
- 进行非特征点判别时大量的点被丢弃
- 检测到的很多特征点都是相邻的
前 3 个问题可以通过机器学习的方法解决,最后一个问题可以使用非最大值抑制的方法解决。
<机器学习的角点检测>
1. 选择一组训练图片(最好是跟最后应用相关的图片)
2. 使用 FAST 算法找出每幅图像的特征点,对图像中的每一个特征点,将其周围的 16 个像素存储构成一个向量P。
3. 每一个特征点的 16 像素点都属于下列三类中的一种
4. 根据这些像素点的分类,特征向量 P 也被分为 3 个子集:Pd ,Ps ,Pb,
5. 定义一个新的布尔变量Kp,如果 p 是角点就设置为 Ture,如果不是就设置为 False。
6. 利用特征值向量p,目标值是Kp,训练ID3 树(决策树分类器)。
7. 将构建好的决策树运用于其他图像的快速的检测。
<非极大值抑制>
在筛选出来的候选角点中有很多是紧挨在一起的,需要通过非极大值抑制来消除这种影响。
为所有的候选角点都确定一个打分函数V,V的值可这样计算:先分别计算Ip与圆上16个点的像素值差值,取绝对值,再将这16个绝对值相加,就得到了V的值
最后比较毗邻候选角点的 V 值,把V值较小的候选角点pass掉。
FAST算法的思想与我们对角点的直观认识非常接近,化繁为简。FAST算法比其它角点的检测算法快,但是在噪声较高时不够稳定,这需要设置合适的阈值。
<实现步骤一、实例化一个FAST对象>
API:fast = =cv.FastFeatureDetector_create( threshold, nonmaxSuppression)
参数:
- threshold:阈值t,有默认值10
- nonmaxSuppression:是否进行非极大值抑制,默认值True
返回:
- Fast:创建的FastFeatureDetector对象
<实现步骤二、利用fast.detect检测关键点,没有对应的关键点描述>
API:kp = fast.detect(grayImg, None)
参数:- gray: 进行关键点检测的图像,注意是灰度图像
返回:- kp: 关键点信息,包括位置,尺度,方向信息
<实现步骤三、将关键点检测结果绘制在图像上,与在sift中是一样的>
API:cv.drawKeypoints(image, keypoints, outputimage, color, flags)
# 示例、FAST角点检测
# OpenCV使用的是传统方法,并没有使用机器学习的方法
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 主函数
if __name__ == '__main__':
# 1 读取图像
img = cv.imread('tv.jpg')
# 2 Fast角点检测
# 2.1 创建一个Fast对象,传入阈值,注意:可以处理彩色空间图像
fast = cv.FastFeatureDetector_create(threshold=30)
# 2.2 检测图像上的关键点
kp = fast.detect(img, None)
# 2.3 在图像上绘制关键点
img2 = cv.drawKeypoints(img, kp, None, color=(0, 0, 255))
# 2.4 输出默认参数
print("Threshold: {}".format(fast.getThreshold()))
print("nonmaxSuppression:{}".format(fast.getNonmaxSuppression()))
print("neighborhood: {}".format(fast.getType()))
print("Total Keypoints with nonmaxSuppression: {}".format(len(kp)))
# 2.5 关闭非极大值抑制
fast.setNonmaxSuppression(0)
kp = fast.detect(img, None)
print("Total Keypoints without nonmaxSuppression: {}".format(len(kp)))
# 2.6 绘制为进行非极大值抑制的结果
img3 = cv.drawKeypoints(img, kp, None, color=(0, 0, 255))
# 3 绘制图像
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(10, 8), dpi=100)
axes[0].imshow(img2[:, :, ::-1])
axes[0].set_title("加入非极大值抑制")
axes[1].imshow(img3[:, :, ::-1])
axes[1].set_title("未加入非极大值抑制")
plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
<原理>
SIFT和SURF算法是受专利保护的,在使用他们时我们是要付费的,但是ORB(Oriented Fast and Rotated Brief)不需要,它可以用来对图像中的关键点快速创建特征向量,并用这些特征向量来识别图像中的对象。
<1、算法流程>
ORB算法结合了Fast和Brief算法,提出了构造金字塔,为Fast特征点添加了方向,从而使得关键点具有了尺度不变性和旋转不变性。具体流程描述如下:
- 构造尺度金字塔,金字塔共有n层,与SIFT不同的是,每一层仅有一幅图像。第s层的尺度为:
σ0是初始尺度,默认为1.2,原图在第0层。
第s层图像的大小:
- 在不同的尺度上利用Fast算法检测特征点,采用Harris角点响应函数,根据角点的响应值排序,选取前N个特征点,作为本尺度的特征点。
- 计算特征点的主方向,计算以特征点为圆心半径为r的圆形邻域内的灰度质心位置,将从特征点位置到质心位置的方向做特征点的主方向。
- 为了解决旋转不变性,将特征点的邻域旋转到主方向上利用Brief算法构建特征描述符,至此就得到了ORB的特征描述向量。
<2、BRIEF算法>
BRIEF是一种特征描述子提取算法,并非特征点的提取算法,一种生成**二值**化描述子的算法,不提取代价低,匹配只需要使用简单的汉明距离(Hamming Distance)利用比特之间的异或操作就可以完成。因此,时间代价低,空间代价低,效果还挺好是最大的优点。
**算法的步骤介绍如下**:
1. 图像滤波:原始图像中存在噪声时,会对结果产生影响,所以需要对图像进行滤波,去除部分噪声。
2. 选取点对:以特征点为中心,取S*S的邻域窗口,在窗口内随机选取N组点对,一般N=128,256,512,默认是256,关于如何选取随机点对,提供了五种形式,结果如下图所示:
- x,y方向平均分布采样
- x,y均服从Gauss(0,S^2/25)各向同性采样
- x服从Gauss(0,S^2/25),y服从Gauss(0,S^2/100)采样
- x,y从网格中随机获取
- x一直在(0,0),y从网格中随机选取
图中一条线段的两个端点就是一组点对,其中第二种方法的结果比较好。
3. 构建描述符:假设x,y是某个点对的两个端点,p(x),p(y)是两点对应的像素值,则有:
对每一个点对都进行上述的二进制赋值,形成BRIEF的关键点的描述特征向量,该向量一般为 128-512 位的字符串,其中仅包含 1 和 0,如下图所示:
<3、实现>
# 1、实例化ORB对象
orb = cv.xfeatures2d.orb_create(nfeatures)
# 2、利用orb.detectAndCompute()检测关键点并计算
参数:- nfeatures: 特征点的最大数量
kp,des = orb.detectAndCompute(gray,None)
参数:
- gray: 进行关键点检测的图像,注意是灰度图像
返回:
- kp: 关键点信息,包括位置,尺度,方向信息
- des: 关键点描述符,每个关键点BRIEF特征向量,二进制字符串,
# 3、将关键点检测结果绘制在图像上
cv.drawKeypoints(image, keypoints, outputimage, color, flags)
# 示例、ORB角点检测
# OpenCV使用的是传统方法,并没有使用机器学习的方法
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 主函数
if __name__ == '__main__':
# 1 图像读取
img = cv.imread('tv.jpg')
# 2 ORB角点检测
# 2.1 实例化ORB对象
orb = cv.ORB_create(nfeatures=5000)
# 2.2 检测关键点,并计算特征描述符
kp, des = orb.detectAndCompute(img, None)
print(des.shape)
# 3 将关键点绘制在图像上
img2 = cv.drawKeypoints(img, kp, None, flags=0)
# 4. 绘制图像
plt.figure(figsize=(10, 8), dpi=100)
plt.imshow(img2[:, :, ::-1])
plt.xticks([]), plt.yticks([])
plt.show()
# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助
# 示例1:视频的读取与显示
import cv2 as cv
# 程序的运行起点、main()
if __name__ == '__main__':
# 1、打开视频文件
cap = cv.VideoCapture("nascar.mp4") # 读取视频文件
if cap.isOpened(): # 判断视频对象是否打开成功
flag = True
else:
flag = False
# 2、循环显示每帧
while flag:
ret, frame = cap.read() # 循环读取每帧
if ret:
cv.imshow("result_win", frame)
if (cv.waitKey(20) & 0xFF) == ord("q"): # 检测到q按键则退出
break
else:
break
cap.release()
cv.destroyWindow("result_win")
# 示例2:摄像头的读取与显示
import cv2 as cv
# 程序的运行起点、main()
if __name__ == '__main__':
# 1、打开摄像头
cap = cv.VideoCapture(0)
if cap.isOpened():
flag = True
else:
flag = False
# 2、循环显示每帧
while flag:
ret, frame = cap.read() # 循环读取每帧
if ret:
frame = cv.flip(frame, 1) # 图像左右镜像
cv.imshow("result_win", frame)
if (cv.waitKey(10) & 0xFF) == ord("q"): # 检测到q按键则退出
break
else:
break
cap.release()
cv.destroyWindow("result_win")
前言:OpenCV的人脸识别算法(以3.4.1.15为例),其能实现基础的人脸识别,但你使用后会发现,其识别的准确率并不是很好(做毕设没问题),使用的算法也是很老的算法,如果你想要更好更高的准确率,(1)你可以研究更高的OpenCV版本带的算法(高版本商用可能会收费),(2)你可以使用其他的开源算法。我建议使用第二种方式,毕竟深度学习现在很火。
<示例一:识别图像中所有的人脸>
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 人脸检测函数
# def face_detect_demo():
# img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) # 图像转为灰度图
# face_detect = cv.CascadeClassifier("haarcascade_frontalface_alt2.xml") # 加载OpenCV的分类器
# face = face_detect.detectMultiScale(img_gray)
# for x, y, w, h in face:
# cv.rectangle(img, (x, y), (x+w, y+w), color=(0, 0, 255), thickness=2)
# cv.imshow("result", img)
# cv.waitKey(0)
# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
# 1、读取图像
img_src = cv.imread("suoerwei.png", cv.IMREAD_UNCHANGED)
img_gray = cv.cvtColor(img_src, cv.COLOR_BGR2GRAY)
# 2、加载OpenCV分类器
face_detect = cv.CascadeClassifier("haarcascade_frontalface_alt2.xml") # 加载OpenCV自带的分类器
# 3、使用分类器对图像进行检测
face = face_detect.detectMultiScale(img_gray)
# 4、把检测出的结果绘制在原图上
for x, y, w, h in face:
cv.rectangle(img_src, (x, y), (x+w, y+w), color=(0, 0, 255), thickness=2)
# 5、显示检测结果
cv.imshow("result", img_src)
cv.waitKey(0)
# 注意:OpenCV的分类器不止一个,可自行尝试其它的
<示例二:识别摄像头和视频中的所有人脸>
# 示例:识别图像中的所有人脸
import cv2 as cv
# 程序的运行起点、main()
if __name__ == '__main__':
# 0、加载人脸识别器
faces_detect = cv.CascadeClassifier("haarcascade_frontalface_alt.xml")
# 1、打开摄像头
cap = cv.VideoCapture(0)
if cap.isOpened():
flag = True
else:
flag = False
# 2、循环显示每帧
while flag:
ret, frame = cap.read() # 循环读取每帧
if ret:
frame = cv.flip(frame, 1) # 图像左右镜像
# 检测图像中的所有人脸
faces = faces_detect.detectMultiScale(frame) # 检测人脸
for x, y, w, h in faces:
cv.rectangle(frame, (x, y), (x + w, y + w), color=(0, 0, 255), thickness=1)
# 显示对图像的检测结果
cv.imshow("result_win", frame)
if (cv.waitKey(10) & 0xFF) == ord("q"): # 检测到q按键则退出
break
else:
break
cap.release()
cv.destroyWindow("result_win")
<示例三:训练人脸数据,并保存结果>
每个S*文件夹存的是一个人的图像,每个人的图像采集十来张放在文件夹下。。
import os
import cv2 as cv
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
# 添加plot的中文支持
font = {'family': 'SimHei', 'weight': 'bold', 'size': 12}
plt.rc("font", **font)
# 程序的运行起点、main()
if __name__ == '__main__':
targets = [] # 每张图片对应的姓名ID
faces_samples = [] # 每张图片提取的人脸矩阵
faces_detect = cv.CascadeClassifier("haarcascade_frontalface_alt.xml") # 加载人脸识别器
imgRoot = "img_faces_src"
imgPaths = [os.path.join(imgRoot, f) for f in os.listdir(imgRoot)]
index = 0
for imgPath in imgPaths:
for imgFile in os.listdir(imgPath): # 循环每个文件
PIL_img = Image.open(os.path.join(imgPath, imgFile)).convert('L') # 以灰度格式打开
img_numpy = np.array(PIL_img, "uint8") # 转换成np格式
faces = faces_detect.detectMultiScale(img_numpy, scaleFactor=1.1, minNeighbors=5, minSize=(32, 32)) # 检测人脸
print(os.path.join(imgPath, imgFile), "len(faces) =", len(faces))
for x, y, w, h in faces:
s = img_numpy[y:y + h, x:x + w]
s = cv.resize(s, dsize=(128, 128))
targets.append(index)
faces_samples.append(s)
index = index + 1
# 训练数据并保存训练结果
recognizer = cv.face.LBPHFaceRecognizer_create() # LBPH训练器
recognizer.train(faces_samples, np.array(targets))
recognizer.write("trainer/trainer.yal")
<示例四:使用训练好的人脸数据进行人脸识别>
import cv2 as cv
# 程序的运行起点、main()
if __name__ == '__main__':
recogizer = cv.face.LBPHFaceRecognizer_create()
recogizer.read("trainer/trainer.yal")
face_detector = cv.CascadeClassifier("haarcascade_frontalface_alt.xml")
cap = cv.VideoCapture(0) # 输入0打开默认摄像头,输入视频地址则打开视频文件
if cap.isOpened():
flag = True
else:
flag = False
while flag:
flag, frame = cap.read()
if not flag:
break
frame = cv.flip(frame, 1)
img_gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY) # 图像转为灰度图
face = face_detector.detectMultiScale(img_gray, scaleFactor=1.1, minNeighbors=5, minSize=(10, 10))
for x, y, w, h in face:
s = img_gray[y:y + h, x:x + w]
s = cv.resize(s, dsize=(128, 128))
ids, confidence = recogizer.predict(s)
cv.rectangle(frame, (x, y), (x + w, y + w), color=(0, 0, 255), thickness=1)
if confidence > 80:
cv.putText(frame, "unknown" + ":%.2f" % confidence, (x + 10, y + 10), cv.FONT_HERSHEY_SIMPLEX, 0.75, (0, 255, 0), 1)
else:
cv.putText(frame, str(ids) + ":%.2f" % confidence, (x + 10, y + 10), cv.FONT_HERSHEY_SIMPLEX, 0.75, (0, 255, 0), 1)
cv.imshow("re", frame)
key = (cv.waitKey(20) & 0xFF)
if key == ord('q'): # 按q键退出
break
cv.destoryAllWindows()
cap.release()