- 图像基础学习
- 图像基本运算
- 基本图形的绘制
- 图像基本绘制
- 图像基本变换
- 滤波器
- 形态学
- 轮廓查找
- 特征点检测与匹配
- 图像的分割和修复
- 机器学习
- 添加水印
- 车辆检测
- 图像拼接
- 车牌识别
- 人脸识别
- 图像分类
- OpenCV的机制
- 机器学习 -- 如何训练模型
- ffmpeg -- 滤波器
- webRT -- 实时通讯领域
- OpenGL --图形学的课程,与图像学的课程 是属于不同概念
python -m pip --version
python -m pip install numpy matplotlib opencv-python
python -m pip install --upgrade pip
import cv2
img = cv2.imread('./public/images/zheng.png')
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.namedWindow('NEW', cv2.WINDOW_AUTOSIZE)
cv2.imshow('img', 0)
import cv2
cv2.namedWindow('NEW', cv2.WINDOW_NORMAL)
cv2.imshow('NEW', cv2.imread('./public/images/zheng.png'))
key = cv2.waitKey(0)
if (key & 0xFF == ord('q')):
cv2.destroyAllWindows()
import cv2
cv2.namedWindow('NEW', cv2.WINDOW_NORMAL)
img = cv2.imread('./public/images/zheng.png')
while True:
cv2.imshow('NEW', img)
key = cv2.waitKey(0)
if (key & 0xFF == ord('q')):
break
elif (key & 0xFF == ord('c')):
cv2.imwrite('./public/images/write.png', img)
else:
print(key)
cv2.destroyAllWindows()
import cv2
cv2.namedWindow('video', cv2.WINDOW_NORMAL)
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
cv2.imshow('video', frame)
key = cv2.waitKey(1)
if (key & 0xFF == ord('q')):
break
else:
print(key)
cap.release()
cv2.destroyAllWindows()
import cv2
fourcc = cv2.VideoWriter_fourcc(*'MJPG')
vw = cv2.VideoWriter('./out.mp4', fourcc, 25, (640, 360))
cv2.namedWindow('video', cv2.WINDOW_NORMAL)
cap = cv2.VideoCapture(0)
while cap.isOpened():
ret, frame = cap.read()
if (ret):
cv2.imshow('video', frame)
vw.write(frame)
key = cv2.waitKey(1)
if (key & 0xFF == ord('q')):
break
else:
print(key)
else:
break
cap.release()
vw.release()
cv2.destroyAllWindows()
import cv2
import numpy as np
def mouse_call_back(event, x, y, flags, userdata):
print(event, x, y, flags, userdata)
cv2.namedWindow('mouse', cv2.WINDOW_NORMAL)
cv2.resizeWindow('mouse', 640, 360)
cv2.setMouseCallback('mouse', mouse_call_back, '123')
img = np.zeros((360, 640, 3), np.uint8)
while True:
cv2.imshow('mouse', img)
key = cv2.waitKey(1)
if key & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
import cv2
import numpy as np
def call_back(value):
print(value)
cv2.namedWindow('trackbar', cv2.WINDOW_NORMAL)
cv2.createTrackbar('R', 'trackbar', 0, 255, call_back)
cv2.createTrackbar('G', 'trackbar', 0, 255, call_back)
cv2.createTrackbar('B', 'trackbar', 0, 255, call_back)
img = np.zeros((480, 360, 3), np.uint8)
while True:
r = cv2.getTrackbarPos('R', 'trackbar')
g = cv2.getTrackbarPos('G', 'trackbar')
b = cv2.getTrackbarPos('B', 'trackbar')
img[:] = [r, g, b]
cv2.imshow('trackbar', img)
key = cv2.waitKey(10)
if key & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
import cv2
cv2.namedWindow('color', cv2.WINDOW_NORMAL)
img = cv2.imread('./public/images/HSV001.png')
def callback(values):
print(values)
colorspaces = [cv2.COLOR_BGR2RGB, cv2.COLOR_BGR2RGBA,
cv2.COLOR_RGB2GRAY, cv2.COLOR_BGR2HSV_FULL]
img = cv2.imread('./public/images/HSV001.png')
cv2.createTrackbar('curcolor', 'color', 0, len(colorspaces), callback)
while True:
index = cv2.getTrackbarPos('curcolor', 'color')
cvt_img = cv2.cvtColor(img, colorspaces[index-1])
cv2.imshow('color', cvt_img)
key = cv2.waitKey(10)
if key & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
import cv2
import numpy as np
a = np.array([2, 3, 4])
c = np.array([[1, 2], [3, 4]])
print(a, '\n', c)
d = np.zeros((4, 8, 6), np.uint8)
print(d)
f = np.ones((4, 8, 6), np.uint8)
print(f)
e = np.full((4, 8, 6), 255, np.uint8)
print(e)
g = np.identity(3)
print(g)
s = np.eye(3, 6, k=2)
print(s)
'''
创建矩阵
检索与赋值[y,x]
从 0 开始 ,注意和常见的[x,y] 有所区别
[y,x,channel] channel 通道数,对于彩色图像来说是3层的,灰色是1层的
获取子数组[:,:]
'''
img = np.zeros((480, 640, 3), np.uint8)
print(img[100][100])
count = 0
while count < 200:
img[count, 100] = 255
img[count, 100, 0] = 255
img[count, 100, 1] = 255
count = count + 1
img[count, 100] = [0, 0, 255]
roi = img[100:200, 100:200]
roi[:, :] = [0, 0, 255]
cv2.imshow('img', roi)
key = cv2.waitKey(0)
if key & 0xFF == ord('q'):
cv2.destroyAllWindows()
'''
[y1:y2,x1:x2] 获取子矩阵
[:,:]获取矩阵的整个矩阵
'''
'''
class cv_exports mat{
public:
int dims: 维数
int rows,cols ;行列数
uchar * data; 存储数据的指针
int *refcount; 引用计数
depth: 像素的位数 ,彩色是8位数据
size: 矩阵的大小
channel: 通道数,RGB是3
type: dep+dt+chs CV_8UC3
};
'''
'''
cv::Mat::clone()
cv::Mat::copyTo()
copy() 接口 ,底层会调用上述的两个深拷贝接口
'''
import cv2
import numpy as np
img = cv2.imread('./public/images/zheng.png')
img2 = img
img3 = img.copy()
img[10:100, 10:100] = [0, 0, 255]
cv2.imshow('img', img)
cv2.imshow('img2', img2)
cv2.imshow('img3', img3)
print(img.shape)
print(img.size)
print(img.dtype)
'''
split(mat)
merge((ch1,ch2,...))
'''
img4 = np.zeros((480, 360, 3), np.uint8)
cv2.imshow('img4', img4)
b, g, r = cv2.split(img4)
b[10:100, 10:100] = 255
cv2.imshow('b', b)
new = cv2.merge((b, g, r))
cv2.imshow('new', new)
cv2.waitKey(0)
'''
line(img,开始点,结束点,颜色,线宽,线型)
ellipse(img,中心点,(长的一半,宽的一半),倾斜角度,从哪个角度开始,到哪个角度结束,颜色,线宽,线性)
polylines(img,点集(32位的),是否闭环,颜色,...)
画文本
putText(img,字符串,起始点,字体,字号,...)
字体:0 1 2 3 4 5 6 7 8 9 ...16
'''
import numpy as np
import cv2
img = np.zeros((600, 600, 3), np.uint8)
cv2.line(img, (10, 30), (100, 300), (255, 255, 255), 10, 4)
cv2.rectangle(img, (10, 10), (100, 100), (255, 255, 255), -1)
cv2.circle(img, (320, 240), 100, (255, 255, 255))
cv2.circle(img, (320, 240), 5, (255, 255, 255), -1)
cv2.ellipse(img, (120, 180), (60, 100), 50, 0, 180, (255, 255, 255), -1)
pts = np.array([(300, 10), (150, 100), (450, 100)], np.int32)
cv2.polylines(img, [pts], True, (0, 255, 255))
cv2.fillPoly(img, [pts], (255, 255, 0))
cv2.putText(img, 'English ...', (60, 60),
cv2.FONT_ITALIC, 3, (255, 255, 255))
cv2.imshow('draw', img)
cv2.waitKey(0)
import cv2
import numpy as np
shape = 0
start = (0, 0)
cv2.namedWindow('mouse', cv2.WINDOW_NORMAL)
img = np.zeros((360, 640, 3), np.uint8)
def mouse_callback(event, x, y, flags, userdata):
global shape, start
print(x, y)
if (event & cv2.EVENT_LBUTTONDOWN == cv2.EVENT_LBUTTONDOWN):
start = (x, y)
elif (event & cv2.EVENT_LBUTTONUP == cv2.EVENT_LBUTTONUP):
if shape == 0:
cv2.line(img, start, (x, y), (0, 0, 255))
elif shape == 1:
cv2.rectangle(img, start, (x, y), (0, 0, 255))
elif shape == 2:
a = (x - start[0])
b = (y - start[1])
r = int((a**2 + b**2) ** 0.5)
cv2.circle(img, start, r, (0, 0, 255))
else:
print('error')
cv2.setMouseCallback('mouse', mouse_callback, "123")
while True:
cv2.imshow('mouse', img)
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
break
elif key == ord('c'):
shape = 2
elif key == ord('r'):
shape = 1
elif key == ord('l'):
shape = 0
cv2.destroyAllWindows()
import cv2
import numpy as np
hsv0001 = cv2.imread('./public/images/HSV001.png')
print(hsv0001.shape)
img = np.ones((hsv0001.shape[0], hsv0001.shape[1], 3), np.uint8) * 50
result = cv2.add(hsv0001, img)
result2 = cv2.subtract(result, img)
result3 = cv2.multiply(hsv0001, img)
result4 = cv2.divide(hsv0001, img)
cv2.imshow('result2', result2)
cv2.imshow('result3', result3)
cv2.imshow('result4', result4)
cv2.imshow('origin', hsv0001)
cv2.imshow('result', result)
'''
A 图像 B 图像
alpha beta 是权重
addWeighted(A ,alpha,B,bate,gamma)
gamma 静态权重
'''
i3 = cv2.addWeighted(hsv0001, 0.7, img, 0.3, 0)
cv2.imshow('i3', i3)
'''
没有进位
与 只有两个为1 结果才为1
或 只要由一个1 结果就为1
非 取反
异或 只有两个不一样才为1 不然为0
'''
n1 = np.zeros((500, 500), np.uint8)
n1[200:300, 200:300] = 255
n2 = np.zeros((500, 500), np.uint8)
n2[250:350, 250:350] = 255
cv2.imshow('n1', n1)
cv2.imshow('n2', n2)
not_i = cv2.bitwise_not(n1)
and_i = cv2.bitwise_and(n1, n2)
or_i = cv2.bitwise_or(n1, n2)
xor_i = cv2.bitwise_xor(n1, n2)
cv2.imshow('not', not_i)
cv2.imshow('and', and_i)
cv2.imshow('or', or_i)
cv2.imshow('xor', xor_i)
cv2.waitKey(0)
'''
图像的缩放
图像翻转
图像旋转
'''
'''
改变的是窗口内图像的大小
resize(src,dst,dsize,fx,fy,interpolation)
x轴的缩放因子
y轴的缩放因子
定义了dsize,就不需要定义fx fy
interpolation: 插值算法
Inter_nearest : 邻近插值,速度快,效果差
inter_linear : 双线性插值,原图中的4个点,默认算法
inter_cubic :三次插值,原图中的16个点,但是花费时间比较长
inter_area :效果最好,但是时间较长
'''
import cv2
import numpy as np
img = cv2.imread('./public/images/zheng.png')
new = cv2.resize(img, (int(
img.shape[1] * 0.5), int(img.shape[0] * 0.5)), None, interpolation=cv2.INTER_AREA)
cv2.imshow('new', new)
'''
图像反转
flip(img,flipCode)
0 上下翻转
> 0 左右翻转
<0 上下+左右翻转
'''
fanzhuan = cv2.flip(new, 2)
cv2.imshow('fanzhuan', fanzhuan)
'''
图像旋转
rotate(img,rotateCode)
顺时针90度 180度旋转 逆时针90度
ROTATE_90_CLOCKWISE
ROTATE_180
ROTATE_90_COUNTERCLOCKWISE
'''
xuanzhuan = cv2.rotate(new, cv2.ROTATE_90_CLOCKWISE)
cv2.imshow('xuanzhuan', xuanzhuan)
'''
图像的仿射变换
仿射变换是图像旋转、旋转、平移的总称
warpAffine(src,M,dsize,flags,mode,value)
M: 变换矩阵(重要)
dsize:变换后的大小
flags: 与resize中的插值算法一致 (一般采用默认值)
mode: 边界外推法标志 (一般采用默认值)
value: 填充边界的值(一般采用默认值)
'''
'''
平移矩阵
矩阵中的每个元素由(x,y) 组成
如果是横向平移,则是在 x上增加数
如果是纵向平移,则是在 y 上增加数
因此,其变换矩阵是 2x2 的矩阵
平移向量为2x1 的向量,所在平移矩阵为2x3矩阵
6:56
'''
'''
https://blog.csdn.net/lz0499/article/details/90578545
说明:[[1,0],[0,1]] 表示 一个 2x2的单元矩阵。100,200表示偏移量,表示x 和 y的偏移量
'''
M = np.float32([[1, 0, 100], [0, 1, 200]])
h, w, ch = new.shape
pingyi = cv2.warpAffine(new, M, (w, h))
cv2.imshow('pingyi', pingyi)
'''
变换矩阵
getRotationMatrix2D(center,angle,scale)
获取矩阵M ,而不需要自己去计算
center 中心点
angle 角度-按逆时针
scale 缩放比例
'''
M2 = cv2.getRotationMatrix2D((w/2, h/2), 45, 0.75)
print(M2)
bianhuan = cv2.warpAffine(new, M2, (w, h))
cv2.imshow('bianhuan', bianhuan)
'''
第二中变换矩阵
由直角三角形的三个点获得的新的变换矩阵
getAffineTransform(src[],dst[])
'''
src = np.float32([[40, 30], [80, 30], [40, 30]])
dst = np.float32([[20, 40], [60, 50], [15, 30]])
bianhuan2 = cv2.getAffineTransform(src, dst)
cv2.imshow('bianhuan2', bianhuan2)
'''
透视变换
将一种坐标系变换为另一种坐标系
warpPerspective(img,M,dsize)
M 是变换矩阵
dsize是目标图像的大小
# 获取M , src dst 分别为四个点
getPersectiveTransform(src,dst)
'''
src = np.float32([[100, 110], [150, 160], [0, 50], [600, 800]])
dst = np.float32([[60, 80], [200, 250], [50, 100], [699, 899]])
M3 = cv2.getPerspectiveTransform(src, dst)
toushi = cv2.warpPerspective(new, M3, (w, h))
cv2.imshow('toushi', toushi)
cv2.waitKey(0)
'''
一幅图像通过滤波器得到另一幅图像;
其中滤波器又称为卷积核,滤波的过程称为卷积
对图像进行扫描和乘法
卷积:原始图像与矩阵进行乘法运算
卷积的几个概念
卷积核的大小
锚点
边界扩充
步长
![]('./public/images/lvbo001.png')
![]('./public/images/lvbo002.png')
卷积核
卷积核一般为奇数,如3x3、5x5、7x7 等
一方面是增加padding的原因;
另一方面是保证锚点在中间,防止位置发生偏移的原因
卷积核大小的影响
在深度学习种,卷积核越大,看到的信息(感受野)越多
提取的特征越好,同时计算量也就越大
锚点
卷积核的正中心就是锚点
![]('./public/images/lvbo003.png')
边界扩充
当卷积核大于1 且不进行边界扩充,输出尺寸将相应缩小
当卷积核以标准方式进行边界扩充,则输出数据的空间尺寸将与输入相等
N = ( W - F +2P)/S +1
N 输出图像大小
W 源图大小
F 卷积核大小
P 扩充尺寸
S 步长大小,默认是1
低通滤波 和高通滤波
低通滤波可以去除噪音或平滑图像
高通滤波可以帮助查找图像的边缘,进行物体识别
图像卷积
filter2D(src,ddepth,kernel,anchor,delta,borderType)
ddepth:位深 ,当值为-1 的时候,表示输出的位深和输入的位深是一致的
kernel:卷积核 当卷积为低通滤波 ,当卷积为高通滤波,
anchor :锚点: 当值为-1 时候,则是输入的核对应的标准锚点
delta: 当卷积之后,可以相加的值,一般为0
borderType :边缘
'''
import cv2
import numpy as np
img = cv2.imread('./public/images/zheng.png')
kernel = np.ones((5, 5), np.float32) / 25
dst = cv2.filter2D(img, -1, kernel)
cv2.imshow('dst', dst)
'''
方盒滤波和均值滤波
这两种滤波器的卷积核是固定的.
('./public/images/001-方盒滤波卷积核.png')
参数a 是未知数
normalize = true , a = 1/W * H 均值滤波
normalize = false ,a = 1
当normalize == true 时, 方盒滤波 =平均滤波
一般取 normalize = true ,即方盒滤波一般是指 平均滤波
所以一般都是调用平均滤波的API
方盒滤波API
boxFilter(src ,ddepth,ksize,anchor,normalize,borderType)
#参数 原图,位深,卷积核大小,锚点,正常是否,边缘
当normalize =true时
均值滤波API
blur(src,ksize,anchor,borderType)
'''
fanghe = cv2.blur(img, (5, 5))
cv2.imshow('fanghe', fanghe)
'''
高斯滤波
('./public/images/002-高斯滤波.png')
('./public/images/003-高斯滤波.png')
('./public/images/004-高斯滤波.png')
中间权重大,旁边权重低
对高斯噪音进行处理
GaussianBlur ( img, kernel,sigmaX ,sigmaY...)
参数,原图,卷积核,
X,Y:最大范围到中心点有多少误差
('./public/images/005-高斯滤波.png')
https://blog.csdn.net/u013066730/article/details/123112159
'''
gaosi = cv2.GaussianBlur(img, (5, 5), sigmaX=1, sigmaY=1)
cv2.imshow('gaosi', gaosi)
'''
中值滤波
假设有一个数组[1556789],取其中的中间值作为卷积后的结果值
中值滤波的优点是对胡椒噪音效果明显(没错,就是胡椒噪音)
medianBlur(img,ksize)
'''
zhngzhi = cv2.imread('./public/images/006.png')
result = cv2.medianBlur(zhngzhi, 5)
cv2.imshow('zhngzhi', zhngzhi)
cv2.imshow('result', result)
'''
双边滤波
可以保留边缘,同时对边缘内的区域进行平滑处理
作用是进行美颜
之前的滤波不仅对图像区域误内无差别的处理,对边缘也处理.
('./public/images/007-双边滤波.png')
bilateralFilter(img,d,sigmaColor,sigmaSpace)
d: 直径
'''
shuangbian = cv2.bilateralFilter(img, 7, 20, 50)
cv2.imshow('shuangbian', shuangbian)
'''
高通滤波
常见的高通滤波
Sobel(索贝尔)-对噪音过滤很强,效果差,对边缘检测,只能求一个方向的边缘
Scharr(沙尔)-不可改变卷积核大小(3x3) 效果好,对边缘不可检测,只能求一个方向的边缘
Laplacian(拉普拉斯) ,能自己计算两个方向的边缘,但是没有降噪的功能
Solbel 算子
先向x方向求导
再向y方向求导
最终结果: |G| =|Gx|+|Gy|
Sobel(src,ddepth,dx,dy,ksize = 5,scale =1,delta = 0 ,borderType = Border_default)
当ksize = 3 ,则退化为沙尔
dx ,dy 只能设置其中一个为1,另外一个为0
当dx设置为1 时候,求的是对Y轴进行求导,算的是Y轴的边缘
当dy设置为1 时候,求的是对X轴进行求导,算的是X轴的边缘
scale :缩放比例
'''
sobel_old = cv2.imread('./public/images/008.png')
sobel1 = cv2.Sobel(sobel_old, -1, 1, 0, ksize=5)
sobel2 = cv2.Sobel(sobel_old, -1, 0, 1, ksize=5)
cv2.imshow('sobel1', sobel1)
cv2.imshow('sobel2', sobel2)
cv2.imshow('sobel', cv2.add(sobel2, sobel1))
'''
Scharr 沙尔算子
与Sobel类似,只不过使用的kernel值不同,kernel只支持3x3
Scharr只能求x方向或y方向的边缘
可以将更细小的线给识别出来
很少运用
Scharr(src,ddepth,dx,dy,)
'''
'''
Laplacian(拉普拉斯) 算子
缺点:对噪音敏感,一般需要先进行去噪再调用拉普拉斯算子
可以同时求两个方向
Laplacian(img,ddepth,ksize,scale=1,borderType =BORDER_DEFAULT)
'''
lap = cv2.Laplacian(sobel_old, -1)
cv2.imshow('lap', lap)
'''
边缘检测,终极大法
Canny
使用5x5高斯滤波消除噪音
计算图像梯度(0° 45 90 135)
取局部最大值
('./public/images/009-Canny阈值计算.png')
API
Canny(img,minVal,maxVal)
minVal maxVal:
min 越小:边缘越少
max 只要超过最大值,都会认为是边缘,所以,max设置得越小,边缘会越多
min-max 之间的过大,则边缘会越少
'''
canny = cv2.Canny(sobel_old, 100, 200)
cv2.imshow('canny', canny)
cv2.waitKey(0)
'''
基于图像形态进行处理的一些基本方法
这些处理方法基本是对二进制图像进行处理(黑白图像进行处理)
卷积核决定着图像处理后的效果
形态学图像处理
腐蚀:形态缩小
膨胀:形态放大
开运算:先腐蚀,后膨胀
闭运算:先膨胀,后腐蚀
顶帽:
黑帽:
'''
'''
图像二值化(黑白处理)
将图像中的每个像素变成两种值:如0,255
二值化有两种
全局二值化
局部二值化
threshod (img ,thresh,maxVal ,type)
img :图像:最好是灰度图
超过阈值的时候,便是 黑色的点
thresh:阈值 ,低于阈值,则是为0 .高于阈值,则变成maxVal
maxVal: 超过阈值,替换成 maxVal
type: thresh_binary 和 thresh_binary_inv
thresh_trunc
thresh_tozero 和thresh_tozero_inv
'''
import cv2
import numpy as np
img = cv2.imread('./public/images/zheng.png')
huidu = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, dst = cv2.threshold(huidu, 180, 255, cv2.THRESH_BINARY)
cv2.imshow('quan', dst)
'''
自适应阈值
解决全局二值化的缺点:由于光照不均匀以及阴影的存在,,只有一个阈值会使得在阴影处的白色被二值化成黑色
adaptiveThreshold(img,maxVal,adaptiveMethod,Type,blockSize,C)
method:计算阈值的方法
ADAPTIVE_THRESH_MEAN_C :计算邻近区域的平均值
ADAPTIVE_THRESH_GUASSIAN_C 高斯窗口加权平均值 常用
type:thresh-binary 或 thresh-binary_inv
blockSize:自定义区域大小
C 常量,应从计算出的平均值或加权平均值中减去
'''
zishiying = cv2.adaptiveThreshold(
huidu, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 3, 0)
cv2.imshow('zishiying', zishiying)
'''
2、膨胀(dilate)
膨胀就是求局部最大值的操作。从数学角度来说,就是将图像与核进行卷积,计算核B覆盖区域的像素点的最大值,并把这个最大值赋值给参考点指定的元素。这样就会使图像中的高亮区域逐渐增长。
3、腐蚀(erode)
腐蚀和膨胀是相反的操作,腐蚀是求局部最小值的操作。腐蚀操作会使图像中的高亮区逐渐减小。
腐蚀:苹果腐蚀
腐蚀的卷积核都是为1的矩阵
erode(img,kernel,iterations=1)
iterations: 腐蚀的次数
只有两者(黑底白图和卷积核)都是白色的时候,才能编程白色
'''
kernel = np.ones((3, 3), np.uint8)
fushi = cv2.erode(huidu, kernel, 9999)
cv2.imshow('fushi', fushi)
'''
形态学的卷积核
卷积核的类型
getStructuringElement(type,size)
type:Morph_rect ,矩形的,全1
Morph_ellipse 椭圆的矩阵,1组成一个圆形,其他为0
Morph_cross :十字架 的矩阵,中间一列和中间一行是1 ,其他都是0
size: (3,3)、(5,5)
'''
kernel2 = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
kernel3 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
kernel4 = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))
print(kernel2)
print(kernel3)
print(kernel4)
'''
膨胀
卷积核越大,膨胀越大
只要有1 ,就能扩展。
dilate(img,kernel,iterations=1)
'''
r = cv2.dilate(huidu, cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)))
cv2.imshow('r', r)
'''
开运算=腐蚀+膨胀 (作用:腐蚀:先去除噪点,膨胀:恢复因去除噪点造成的损失)
闭运算=膨胀+腐蚀(作用:膨胀:先去除噪点,腐蚀:再恢复因去除噪点造成的损失)
开运算API 消除外部噪点 ,噪点越大,kernel越大
morphologyEx(img,MORPH_OPEN,KERNEL)
闭运算API
('./public/images/015-开运算-消除白噪点.png')
('./public/images/016-闭运算-消除白噪点.png')
消除白底的黑色噪点
morphologyEx(img,MORPH_CLOSE,KERNEL)
'''
kai = cv2.morphologyEx(huidu, cv2.MORPH_OPEN, kernel=kernel)
BI = cv2.morphologyEx(huidu, cv2.MORPH_CLOSE, kernel=kernel)
cv2.imshow('kai', kai)
cv2.imshow('BI', BI)
'''
形态学梯度
梯度 = 原图 - 腐蚀性
morphologyEx(img,MORPH_GRADIENT,kernel)
求:边缘
kernel比较大,腐蚀性比较大。边缘就不是特别清楚
'''
xingtaixue = cv2.morphologyEx(huidu, cv2.MORPH_GRADIENT, kernel=kernel)
cv2.imshow('xing', xingtaixue)
'''
顶帽运算
顶帽 = 原图 - 开运算
morphologyEx(img,MORPH_TOPHAT,kernel)
剩下黑底白色噪点
'''
ding = cv2.morphologyEx(huidu, cv2.MORPH_TOPHAT, kernel=kernel)
cv2.imshow('ding', ding)
'''
黑帽操作
黑帽 = 原图 - 闭运算
剩下白底的黑点
'''
h = cv2.morphologyEx(huidu, cv2.MORPH_BLACKHAT, kernel=kernel)
cv2.imshow('h', h)
'''
总结:
开运算,先腐蚀再膨胀,去除大图形外的小图形
闭运算:先膨胀再腐蚀,去除大图形内的小图形
梯度:求图形的边缘
顶帽:原图-开运算,得到大图形外的小图形
黑帽:原图-闭运算,得到大图形内的小图形
'''
cv2.waitKey(0)
'''
图像轮廓的作用
可以用于图形分析
物体的识别和检测
注意点:
为了检测的准确性,需要先对图像进行二值化或Canny操作
画轮廓时会修改输入的图像
轮廓查找的API
findContours(img,mode,ApproximationMode...)
模式-mode:
Retr_external=0,表示之检测外轮廓
Retr_list=1,检测的轮廓不建立等级关系-常用
Retr_ccomp = 2 ,每层最多两级
Retr_tree = 3 ,按树形存储轮廓-常用
('./public/images/020-轮廓-EXTERNAL.png')
('./public/images/021-轮廓-LIST.png)
('./public/images/022-轮廓-CCOMP.png')
('./public/images/023-轮廓-TREE.png')
近似模式-ApproximationMode:
chain_approx_none :保存所有轮廓上的点
CHAIN_APPROX_SIMPLE 只保存角点
两个返回值:
contours 和hierarchy
contours:所有轮廓
hierarchy:轮廓间的层级关系
'''
'''
查找轮廓
'''
import cv2
origin = cv2.imread('./public/images/zheng.png')
cv2.imshow('origin', origin)
huidu = cv2.cvtColor(origin, cv2.COLOR_BGR2GRAY)
cv2.imshow('huidu', huidu)
print(origin.shape)
print(huidu.shape)
ret, binary = cv2.threshold(huidu, 150, 255, cv2.THRESH_BINARY)
cv2.imshow('bin', binary)
contours, hierarchy = cv2.findContours(
binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
'''
drawContours(img,contours,controurIdx,color,thickness)
contourIdx :设置顺序号, -1 表示绘制所有轮廓
color:轮廓颜色
thicknes:线宽,-1 是全部填充
'''
new = cv2.drawContours(origin, contours, -1, (255, 255, 255), 1)
cv2.imshow('new', new)
'''
轮廓的面积和周长
contourArea(contour)
contour:轮廓
arcLength(curve,closed)
closed:是指闭合的还是非闭合的
'''
area = cv2.contourArea(contours[2])
print(area)
length = cv2.arcLength(contours[2], True)
print(length)
'''
多边形逼近与凸包
('./public/images/024-多边形逼近与凸包.png')
API
approxPolyDP(curve,epsilon,closed)
curve:轮廓
epsilion: 精度
closed:是否是闭合的轮廓
convexHull(points,clockwise,..)
clockwise:顺时针绘制
'''
hand = cv2.imread('./public/images/025-hand.png')
hand_gray = cv2.cvtColor(hand, cv2.COLOR_BGR2GRAY)
hand_ret, hand_bin = cv2.threshold(hand_gray, 150, 255, cv2.THRESH_BINARY)
hand_con, hand_hierarchy = cv2.findContours(
hand_bin, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
hand_cond = cv2.drawContours(hand, hand_con, 0, (255, 255, 255), 1)
cv2.imshow('hand_cond', hand_cond)
print('hand_cond_len', hand_cond)
approx = cv2.approxPolyDP(hand_con[1], 20, True)
def drawShape(src, points):
i = 0
while i < len(points):
if (i == len(points)-1):
x, y = points[i][0]
x1, y1 = points[0][0]
cv2.line(src, (x, y), (x1, y1), (255, 255, 255), 1)
else:
x, y = points[i][0]
x1, y1 = points[i+1][0]
cv2.line(src, (x, y), (x1, y1), (255, 255, 255), 1)
i = i+1
drawShape(hand, approx)
'''
外接矩阵
最大外接矩阵
最小外接矩阵
('./public/images/026-最大最小外接矩阵.png')
minAreaRect(points)
points:轮廓
返回值
RotatedRect : 是一个结构体,旋转的矩阵,其中的角度非常关键 其中包括
x,y 起始点
width,height
angle:角度
boundingRect(array)
array:轮廓
返回值: Rect
x,y 起始点
width,height
'''
cv2.waitKey(0)
import cv2
import numpy as np
'''
外接矩阵
最大外接矩阵
最小外接矩阵
('./public/images/026-最大最小外接矩阵.png')
minAreaRect(points)
points:轮廓
返回值
RotatedRect : 是一个结构体,旋转的矩阵,其中的角度非常关键 其中包括
x,y 起始点
width,height
angle:角度
boundingRect(array)
array:轮廓
返回值: Rect
x,y 起始点
width,height
'''
hand = cv2.imread('./public/images/027-waijiejuzhen.png')
hand_gray = cv2.cvtColor(hand, cv2.COLOR_BGR2GRAY)
hand_ret, hand_bin = cv2.threshold(hand_gray, 150, 255, cv2.THRESH_BINARY)
hand_con, hand_hierarchy = cv2.findContours(
hand_bin, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
r = cv2.minAreaRect(hand_con[1])
box = cv2.boxPoints(r)
box = np.int0(box)
cv2.drawContours(hand, [box], 0, (255, 255, 255), 2)
x, y, w, h = cv2.boundingRect(hand_con[1])
cv2.rectangle(hand, (x, y), (x+w, y+h), (255, 255, 0), 2)
cv2.imshow('hand', hand)
cv2.waitKey(0)
'''
加载视频
通过形态学识别车辆
对车辆进行统计
显示车辆统计信息
去背景
createBackgroundSubtractorMOG(...)
history = 200
预存 多少位帧 有可以判断作为背景
python -m pip install opencv-contrib-python --user
'''
import cv2
import numpy as np
bgsubmog = cv2.bgsegm.createBackgroundSubtractorMOG()
cap = cv2.VideoCapture('./public/vedio/che.mp4')
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
line_hight = 380
offsert = 6
cars = []
carno = 0
def center(x, y, w, h):
x1 = int(w/2)
y1 = int(h/2)
cx = x+x1
cy = y + y1
return cx, cy
while True:
ret, frame = cap.read()
if (ret):
huidu = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(huidu, (5, 5), 5)
mask = bgsubmog.apply(blur)
erode = cv2.erode(mask, kernel)
dilate = cv2.dilate(erode, kernel, iterations=3)
close = cv2.morphologyEx(dilate, cv2.MORPH_CLOSE, kernel)
con, cengji = cv2.findContours(
close, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cv2.line(frame, (10, line_hight),
(480, line_hight), (255, 255, 255), 3)
for (i, c) in enumerate(con):
(x, y, w, h) = cv2.boundingRect(c)
cv2.rectangle(frame, (x, y), (x+w, y+h),
(255, 255, 255), 2)
if (w > 25 and h > 25):
cpoint = center(x, y, w, h)
cars.append(cpoint)
for (x, y) in cars:
if ((y > line_hight - offsert) and (y < line_hight+offsert)):
carno += 1
cars.remove((x, y))
print(carno)
cv2.putText(frame, "Cars Count:"+str(carno), (50, 50),
cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 1)
cv2.imshow('frame', frame)
key = cv2.waitKey(1)
if (key == 27):
break
cap.release()
cv2.destroyAllWindows()
'''
应用场景:
图像搜索,如以图搜图
拼图游戏
图像拼接,将两长有关联的图拼接到一起
拼图方法
* 寻找特征
* 特征是唯一的
* 可追踪的
* 能比较的
* 平坦部分很难找到它再原图中的位置
* 边缘相比平坦要号召一些,但也不能一下确定
* 角点可以一下就能找到其再原图中的位置
什么是特征
图像特征就是指有意义的图像区域,具有独特性,易于辨识性,比如角点,,斑点 以及高密度区
角点:
* 灰度梯度的最大值对应的像素
* 两条线的交点
* 极值点(一阶导数最大值,但二阶导数为0)
Harris角点
('./public/images/028-Harris.png')
* 光滑地区,无论向哪里移动,衡量系数不变
* 边缘地址,垂直边缘移动时,衡量系统变化剧烈
* 在焦点处,无论往哪个方向移动,衡量系统都变化剧烈
API
cornelHarris(img,blockSize,ksize,k)
blockSize:检测窗口大小
ksize:SObel卷积核
k:权重系系数,经验值,一般取0.02-0.01
返回值:
所有角点
'''
import cv2
import numpy as np
img = cv2.imread('./public/images/zheng.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
harris = cv2.cornerHarris(gray, blockSize=2, ksize=3, k=0.04)
'''
Shi-Tomasi角点
* 对Harris角点加测的改进
* Harris 检测算的稳定性和K值有关,而K是个经验值,不好设定值
API
goodFeaturesToTrack(img,maxCorners,...)
maxCorners:角点的最大数,值为0 表示无限制
qualityLevel: 小于1.0的证书,一般在0.01-0.1之间
minDistance: 角之间最小欧式举例,忽略小于此距离的点
mask: 感兴趣的区域
blockSIze:检测窗口的大小
useHarrisDetector: 是否使用Harris算法,默认值为false,
k:默认是0.04
'''
corners = cv2.goodFeaturesToTrack(
gray, maxCorners=1000, qualityLevel=0.01, minDistance=10)
corners = np.int0(corners)
for i in corners:
x, y = i.ravel()
cv2.circle(img, (x, y), 3, (255, 255, 255), -1)
cv2.imshow('shi', img)
cv2.waitKey(0)
'''
Scale-Invariant Feature Transform
与缩放无关的关键点检测
SIFT出现的原因
* Harris 角点具有旋转不变的特性
* 但缩放后就可能不再是关键点了
使用SIFT 步骤
创建SIFT对象
进行检测 ,kp=sift.detect(img,...)
绘制关键点,cv2.drawKeypoints(gray,kp,img)
'''
import cv2
import numpy as np
img = cv2.imread('./public/images/zheng.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
sift = cv2.SIFT_create()
kp = sift.detect(gray, None)
cv2.drawKeypoints(gray, kp, img)
'''
计算SIFT 描述子
描述子和关键点的区别
关键点:位置,大小和方向
关键点描述子:记录了关键点周围对其有贡献的像素点的一组向量值,其不受仿射变换,光照变换影响
API:
des: 描述子 ,其作用是进行特征匹配
kp: 关键点 keypoints
kp, des = sift.compute(img,kp)
同时计算关键点和描述
kp ,des = sift.detectAndCompute(img,...)
mask:指明对img中哪个区域进行计算
'''
kp2, des2 = sift.detectAndCompute(gray, None)
print(des2)
cv2.imshow('img', img)
cv2.waitKey(0)
'''
Speeded-UP Robust Features
加速的鲁棒性
SIFT最大的问题是速度慢,因此才有SURF
使用SURF 的步骤
surf = cv2.xfeatures2d.SURF_create()
kp ,des = surf.detectAndCompute(img,mask)
'''
import cv2
import numpy as np
img = cv2.imread('./public/images/zheng.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.SURF
kp = sift.detect(gray, None)
cv2.drawKeypoints(gray, kp, img)
'''
Oriented Fast And Rotated Brief
特征点检测与描述子
ORB优势:
ORB 可以做到实时检测
FAST特征点检测: 可以做到特征点的实时检测
brief:是对已检测到的特征点进行描述,它加快了特征描述符建立的速读,同时也极大降低了计算的速读
使用ORB步骤
orb = cv2.ORB_create()
'''
'''
总结:
SIFT :计算准确率最高,速读最慢,能检测出来的关键点和描述子最多欧
SURF:速读比SIFT,计算准确率比SIFT低
ORB:速读最快,能做到实时性,计算准确率最低
'''
import cv2
import numpy as np
img = cv2.imread('./public/images/zheng.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
sift = cv2.ORB_create()
kp, des = sift.detectAndCompute(gray, None)
cv2.drawKeypoints(gray, kp, img)
cv2.imshow('img', img)
cv2.waitKey(0)
'''
BF: Brute-Force 暴力特征匹配方法
FLANN 最快邻近特征匹配方法
暴力特征匹配原理:它使用第一组中的每个特征的描述子,与第二组中的所有特征描述子进行匹配(枚举匹配) 计算他们之间的差距,然后将最近一个匹配返回
OpenCV特征匹配步骤
创建匹配器 BFMatcher (normType,crossCheck )
normType 近似度算法 ,有以下几种
* NORM_L1 用于SIFT 和SURF
* NORM_L2 用于SIFT 和SURF ,默认值
* HAMMING1 用于ORB 算法
* HAMMING2 用于ORB算法
crossCheck 是否进行交叉匹配,默认为false ,开启后计算量会更大
进行特征匹配 bf.match (des1 ,des2 )
des:描述子
回执匹配点:cv2.drawMatches(img1,kp1,img2,kp2)
搜索图 img1 ,kp1
匹配图 img2 kp2
match() 方法返回匹配的结果
'''
import cv2
img1 = cv2.imread('./public/images/zheng.png')
img2 = cv2.imread('./public/images/zheng.png')
g1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
g2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
sift = cv2.SIFT_create()
kp1, des1 = sift.detectAndCompute(g1, None)
kp2, des2 = sift.detectAndCompute(g2, None)
bf = cv2.BFMatcher(cv2.NORM_L2)
match = bf.match(des1, des2)
img3 = cv2.drawMatches(img1, kp1, img2, kp2, match, None)
cv2.imshow('img3', img3)
('./public/images/029-暴力特征匹配效果.png')
cv2.waitKey(0)
'''
在进行批量特征匹配时,FLANN速读更快
由于它使用的邻近近似值,所以精度较差
使用步骤
* 创建FLANN匹配器,FlannBasedMather(index_params)
index_params 字典:匹配算法KDTREE, LSH
kdtree 适用于 SIFT算法 SURF 算法
lsh 适用于orb算法
如果是KDTREE 算法,还需要设置 search_params 字典:经验值:一般为index_params 的10倍
KDTREE : index_params = dict( algorithm = FLANN_INDEX_KDTREE,trees = 5) FLANN_INDEX_KDTREE 实际就是1
search_params = dict (checks = 50 )
* 进行特征匹配 flann.match/knnMatch()
knnMatch(param1,param2)
参数为SIFT SURF ORB 等计算的描述子
k 表示取欧式举例最近的前k个关键字
返回的是一个DMatch对象
distance: 描述子之间的举例,值越低越好
queryIdx 第一个图像的描述子索引值
trainIdx 第二个图的描述子索引值
imgIdx 第二图的索引值
* 绘制匹配点 cv2.drawMathes/drawMatchesKnn(...)
drawMathesKnn
搜索img ,kp
匹配图像img ,kp
match()方法返回的匹配结果
'''
import cv2
import numpy as np
img1 = cv2.imread('./public/images/zheng.png')
img2 = cv2.imread('./public/images/zheng.png')
sift = cv2.SIFT_create()
g1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
g2 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
kp1, des1 = sift.detectAndCompute(g1, None)
kp2, des2 = sift.detectAndCompute(g2, None)
index_params = dict(algorithm=1, trees=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(des1, des2, k=2)
good = []
for i, (m, n) in enumerate(matches):
if m.distance < 0.7 * n.distance:
good.append(m)
ret = cv2.drawMatchesKnn(img1, kp1, img2, kp2, [good], None)
cv2.imshow('re', ret)
cv2.waitKey(0)
'''
特征匹配+ 单应性矩阵
单应性矩阵 :透视变换
'''
import cv2
import numpy as np
img1 = cv2.imread('./public/images/zheng.png')
img2 = cv2.imread('./public/images/zheng.png')
sift = cv2.SIFT_create()
g1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
g2 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
kp1, des1 = sift.detectAndCompute(g1, None)
kp2, des2 = sift.detectAndCompute(g2, None)
index_params = dict(algorithm=1, trees=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(des1, des2, k=2)
good = []
for i, (m, n) in enumerate(matches):
if m.distance < 0.7 * n.distance:
good.append(m)
if len(good) >= 4:
srcPts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)
dstPts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)
H, _ = cv2.findHomography(srcPts, dstPts, cv2.RANSAC, 5.0)
h, w = img1.shape[:2]
pts = np.float32([[0, 0], [0, h-1], [w-1, h-1], [w-1, 0]]
).reshape(-1, 1, 2)
dst = cv2.perspectiveTransform(pts, H)
cv2.polylines(img2, [np.int32(dst)], True, (0, 0, 255))
else:
print('eror')
exit()
ret = cv2.drawMatchesKnn(img1, kp1, img2, kp2, [good], None)
cv2.imshow('re', ret)
cv2.waitKey(0)
'''
读文件并重置尺寸
根据特征点和计算描述子,得到单应性矩阵
图像变换
图像拼接并输出图像
'''
'''
坐标系
此坐标与之前的坐标不同,
('./public/images/034-坐标系.png)
('./public/images/035-图像拼接原图.png)
('./public/images/036-图像拼接结果.png)
('./public/images/037-单应性矩阵变换.png)
('./public/images/038-单应性矩阵变换.png)
('./public/images/039-放大窗口.png)
('./public/images/040-图像平移.png)
('./public/images/041-图像平移.png)
'''
import cv2
import numpy as np
def get_home(img1, img2):
sift = cv2.SIFT_create()
k1, d1 = sift.detectAndCompute(img1, None)
k2, d2 = sift.detectAndCompute(img2, None)
bf = cv2.BFMatcher()
match = bf.knnMatch(d1, d2, 2)
verify_ratio = 0.8
verify_matches = []
for m1, m2 in match:
if m1.distance < 0.8 * m2.distance:
verify_matches.append(m1)
min_matches = 8
if len(verify_matches) > min_matches:
img1_pts = []
img2_pts = []
for m in verify_matches:
img1_pts.append(k1[m.queryIdx].pt)
img2_pts.append(k2[m.queryIdx].pt)
img1_pts = np.float32(img1_pts).reshape(-1, 1, 2)
img2_pts = np.float32(img2_pts).reshape(-1, 1, 2)
H, mask = cv2.findHomography(img1_pts, img2_pts, cv2.RANSAC, 5.0)
return H
else:
print('error : 没有足够的特征匹配点')
exit()
def stitch_image(img1, img2, H):
h1, w1 = img1.shape[:2]
h2, w2 = img2.shape[:2]
img1_dims = np.float32(
[[0, 0], [0, h1], [w1, h1], [w1, 0]]).reshape(-1, 1, 2)
img2_dims = np.float32(
[[0, 0], [0, h2], [w2, h2], [w2, 0]]).reshape(-1, 1, 2)
img1_transform = cv2.perspectiveTransform(img1_dims, H)
result_dims = np.concatenate((img2_dims, img1_transform), axis=0)
[x_min, y_min] = np.int32(result_dims.min(axis=0).ravel()-0.5)
[x_max, y_max] = np.int32(result_dims.min(axis=0).ravel()+0.5)
transform_dist = [-x_min, -y_min]
transform_array = np.array(
[
[1, 0, transform_dist[0]],
[0, 1, transform_dist[1]],
[0, 0, 1]
]
)
result_img = cv2.warpPerspective(
img1, transform_array.dot(H), (x_max - x_min, y_max - y_min))
result_img[transform_dist[1]:transform_dist[1]+h2,
transform_dist[0]:transform_dist[0]+w2] = img2
return result_img
img1 = cv2.imread('./public/images/zheng.png')
img2 = cv2.imread('./public/images/zheng.png')
img1 = cv2.resize(img1, (640, 480))
img2 = cv2.resize(img2, (640, 480))
inputs = np.hstack((img1, img2))
H = get_home(img1, img2)
result_img = stitch_image(img1, img2, H)
cv2.imshow('img', inputs)
cv2.waitKey(0)
'''
('./public/images/045-分水岭法1.png')
('./public/images/046-分水岭法2.png')
('./public/images/047-分水岭法.png')
分水岭法的处理步骤
* 标记背景
* 标记前景
* 标记未知区域
* 进行分割
'''
'''
分水岭API
watershed(img,masker)
masker:包括前景标记,背景标记,未知区域
获取背景:
('./public/images/048-分割硬币-获取背景.png')
('./public/images/049-分割硬币-获取前景.png')
('./public/images/050-分割硬币-未知区域.png')
'''
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('./public/images/coins.webp')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
kernel = np.ones((3, 3), np.int8)
open1 = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, 2)
bg = cv2.dilate(open1, kernel, iterations=1)
dist = cv2.distanceTransform(open1, cv2.DIST_L2, 5)
ret2, fg = cv2.threshold(dist, 0.7 * dist.max(), 255, 0)
unknow = cv2.subtract(bg, np.uint8(fg))
ret, marker = cv2.connectedComponents(np.uint8(fg))
marker = marker + 1
marker[unknow == 255] = 0
result = cv2.watershed(img, marker)
img[result == -1] = [0, 0, 255]
('./public/images/053-分水岭分割最终效果图.png')
cv2.imshow('img', result)
cv2.waitKey()
'''
通过交互的方式(鼠标)获取前景物体.
* 用户指定前景的大体区域,剩下的为背景区域.
* 用户还可以明确指定某些地方为前景或背景
* GrabCut采用分段迭代的方法分析前景物体形成模型树
* 最后根据权重决定某个像素是前景还是背景
[示例]GrabCut
* 主体结构
* 鼠标事件的处理
* 调用GrabCut实现前景和背景的分离
GrabCut API
grabCut(img,,mask,bgdModel,fgdModel,iterrator,mode)
mask: 进行分割之后产生的掩码是多少.得到掩码后,就能将要分割的图片摘取出来.每一个像素值的含义都能了解到
rect: 图片选取的区域
bgdModel:固定值
fgdModel:固定值
iterator ;迭代次数
mask:
BGD 背景 0
FGD 前景 1
PR_BGD 可能是背景 2
PR_FGD 可能是背景 3
bgdModel 是 np.float64 type zero arrays of size(1,65) 64位的浮点型
fgdModel 同上
mode
GC_INIT_WITH_RECT : 指定区域找前景
GC_INIT_WITH_MASK :第一次调用 GC_INIT_WITH_RECT 对前后景进行分离,之后还可以通过 GC_INIT_WITH_MASK 进行迭代
'''
import cv2
import numpy as np
class App:
startX = 0
startY = 0
rect = (0, 0, 0, 0)
flag_rect = False
def onmouse(self, event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN:
self.flag_rect = True
self.startX = x
self.startY = y
print('左键下')
elif event == cv2.EVENT_LBUTTONUP:
cv2.rectangle(self.img, (self.startX, self.startY),
(x, y), (255, 255, 255), 3)
self.flag_rect = False
self.rect = (min(self.startX, x), min(self.startY, y),
abs(self.startX - x), abs(self.startY - y))
print('左键上')
elif event == cv2.EVENT_MOUSEMOVE:
if self.flag_rect == True:
self.img = self.img2.copy()
cv2.rectangle(self.img, (self.startX, self.startY),
(x, y), (0, 255, 0), 3)
print('鼠标挪动')
def run(self):
print('self')
cv2.namedWindow('input')
cv2.setMouseCallback('input', self.onmouse)
self.img = cv2.imread('./public/images/youke.jpg')
self.img2 = self.img.copy()
self.mask = np.zeros(self.img.shape[:2], dtype=np.uint8)
self.output = np.zeros(self.img.shape, np.uint8)
while (1):
cv2.imshow('input', self.img)
cv2.imshow('self', self.output)
k = cv2.waitKey(100)
if k == 27:
break
if k == ord('g'):
bgdModel = np.zeros((1, 65), np.float64)
fgdModel = np.zeros((1, 65), np.float64)
cv2.grabCut(self.img2, self.mask,
self.rect, bgdModel, fgdModel, 1, cv2.GC_INIT_WITH_RECT)
mask2 = np.where((self.mask == 1) | (
self.mask == 3), 255, 0).astype('uint8')
self.output = cv2.bitwise_and(self.img2, self.img2, mask=mask2)
App().run()
'''
MeanShift 原理
严格来说该方法并不是用来对图像分割的,而是在色彩层面的平滑滤波
它会中和色彩分布相近的颜色,平滑色彩细节,侵蚀掉面积较小的颜色区域
它以图像上任一点P为圆心,半径为sp ,色彩幅值 sr 进行不断的迭代
sr 越大 对判断颜色相同的差异要求越低,准确率越低
sr 越小 对判断颜色相同的差异要求越高,准确率越高
('./public/images/054-图像分割-MeanShift 效果图.png')
API
pyrMeanShiftFiltering(img,double sp ,double sr ,maxLevel = 1)
maxLevel 默认值
'''
import cv2
import numpy as np
img = cv2.imread('./public/images/coins.webp')
sp = 20
sr = 30
mean_img = cv2.pyrMeanShiftFiltering(img, sp, sr, 1)
img_canny = cv2.Canny(mean_img, 150, 300)
contours, _ = cv2.findContours(
img_canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img, contours, -1, (255, 255, 255), 2)
cv2.imshow('img', img)
cv2.imshow('mean', mean_img)
cv2.imshow('imgc', img_canny)
cv2.waitKey()
'''
视频前后背景分离
视频背景扣除原理
* 视频是一组连续的帧(一幅幅图组成)
* 帧与帧之间关系密切,组成一组帧,称为 GOP
* 在GOP中,背景几乎是不变的
MOG去除背景
* 混合高斯模型为基础的前景/背景分割算法
* createBackgroundSubtractorMOG(
history,//默认200ms 建立参考帧至少需要200ms
nmixtures,//高斯范围值,默认为5 像素,把一个大的图片分成5x5的小块,每个5x5的小块都有一个高斯值,从而算出参考模型
backgroundRatio,//背景比率,默认0.7 这张图中 70% 属于背景.
nosiseSigma //默认0 自动降噪
)
'''
import cv2
import numpy
cap = cv2.VideoCapture(0)
mog = cv2.createBackgroundSubtractorMOG2()
while (True):
ret, frame = cap.read()
fgmask = mog.apply(frame)
cv2.imshow('img2', frame)
cv2.imshow('img', fgmask)
k = cv2.waitKey(1)
if k == 27:
break
cap.release()
cv2.destroyAllWindows
'''
MOG2去背景
同MOG 类似,不过对亮度产生的阴影有更好的识别,缺点,产生噪点
API
createBackgroundSubtractorMOG2(
history,//500毫秒
...
detectShadows//是否检测阴影,默认为True
GMG去背景
静态背景图像估计和每个像素的贝叶斯分割抗噪性更强
优点;可以计算阴影背景,去除噪点
缺点:采用默认值,则在开始好长时间内是黑屏,取决于帧率.
API:
cv2.createBackgroundSubtractorGMG(
initializationFrames ,//初始帧数,120,这和MOG MOG2 最大的区别
)
)
'''
import cv2
import numpy
cap = cv2.VideoCapture(0)
mog = cv2.bgsegm.createBackgroundSubtractorGMG()
while (True):
ret, frame = cap.read()
fgmask = mog.apply(frame)
cv2.imshow('img2', frame)
cv2.imshow('img', fgmask)
k = cv2.waitKey(1)
if k == 27:
break
cap.release()
cv2.destroyAllWindows
'''
图像修复
('./public/images/055-图像修复效果.png')
API
inpaint(
img,
mask,//与原始图片一样大小的,黑底白色的残缺位置的图片
inpaintRadius,//每个点的圆形领域半径
flags //INPAINT_NS INPAINT_TELEA
)
INPAINT_NS
INPAINT_TELEA 算法简单,破损里的平方加权平均值
# 有点不是很智能,需要找到破损的图片
'''
import cv2
import numpy as np
img = cv2.imread('./public/images/coins.webp')
mask = cv2.imread('./public/images/coins.webp', 0)
dst = cv2.inpaint(img, mask, 5, cv2.INPAINT_TELEA)
cv2.imshow('dst', dst)
cv2.waitKey()
'''
基本概念:
范围,互为包含关系
人工智能 > 机器学习 > 深度学习
计算机视觉与机器学习的关系
计算机视觉是机器学习的一种医用,而且是最有价值的应用
人脸识别
有两种主要的方法
哈儿级联方法(Harr) --cv自带的
深度学习方法(DNN)
哈儿级联法
Harr是专门解决人脸识别而退出的
在深度学习还不流行的时候,Harr已可以商用.
Harr人脸识别步骤
创建Harr级联器
导入图片并将其灰度化
调用detectMultiScale方法进行人脸识别
detectMultiScale(img,
double scaleFactor =1.1 //缩放因子,哈儿级联器,对图片进行扫描,可能扫描的时候不能把人脸扫描进来,对图片进行缩小或进行放大.
int minNeighbors = 3 //最小的像素值,人脸识别的时候最少需要的像素
...
)
优缺点: 可以检测到正面的图片,但是侧面检测不到
'''
import cv2
import numpy as np
facer = cv2.CascadeClassifier()
img = cv2.imread('')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
facer.detectMultiScale(gray, 1.1, 5)
for (x, y, w, h) in facer:
cv2.rectangle(img, (x, y), (x+w, y+h), (0, 0, 255), 2)
cv2.imshow('img', img)
cv2.waitKey()
'''
Harr+Tesseract 车牌识别
Harr能定位到车牌的大体位置.
tesseract 提取数字
]步骤
通过Harr定位车牌的大体位置
对车牌进行预处理
调用tesseract进行文字识别
车牌预处理包括的内容
对车牌进行二值化处理
进行形态学处理
滤波去除噪点
缩放
Tesseract的安装
(https://blog.csdn.net/LOVEmy134611/article/details/119344846)
'''
import cv2
import numpy as np
import pytesseract as py
plate = cv2.CascadeClassifier('')
img = cv2.imread('./')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = plate.detectMultiScale(gray, 1.1, 3)
for (x, y, w, h) in faces:
cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 255), 2)
roi = gray[y:y+h, x:x+w]
ret, roi_bin = cv2.threshold(roi, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
result = py.image_to_string(roi, lang='chi_sim+eng', config='--psm 8 --oem 3')
print(result)
cv2.imshow('roi_bin', roi_bin)
cv2.imshow('img', img)
cv2.waitKey
import cv2
from cv2 import dnn
import numpy as np
'''
* 什么是深度学习
* 几种常见的深度学习网络
* 构建深度学习网络的框架tf ,pytorch
* 进行网络训练的数据集
深度学习基础概念
深度学习是计算机视觉最为重要的方法
* 深度学习在40年代就被剔除,但一直停滞不前
* 2011,微软在语音识别上使用,取得了突破的效果
* 2012 ,DNN在图像识别领域取得惊人的效果,错误率从26% 降低到15%
* 2016 AlphaGo击败人类,引起世界震惊
深度学习网络模型
DNN(Deep Neural Network) 深度神经网络
RNN CNN 是属于一种特殊的DNN
RNN(Recurrent Neural Network) 循环神经网络
CNN(Convolutional Neural Network) 卷积神经网络
RNN用途
* 语音识别
* 机器翻译
* 生成图像描述
CNN:
* 图片分类,检索
* 目标定位检测
* 目标分割
* 人脸识别
几种CNN网络实现
* LeNet 1998 第一代 28x28手写
* AlexNet 2012 ImageNet 比赛的冠军
* 还有这几种: VGG ,GoogleLeNet , ResNet
几种CNN目标检测网络实现
* RCNN ,FastRCNN ,Faster RCNN
* SSD
* YOLO YOLOv2 ... YOLOv5
深度学习库
* tensorflow ,google出品
* caffe -> caffe2 ->torch(pytorch) 贾扬清开源 ->facebook 提供
* MXNet Apache出品
训练数据集 -> 只有经过数据集训练 神经网络 才能成效
* MNIST Fashion-MNIST 手写字母
* VOC 举办挑战赛时的数据集 2012年后欧不再举办
* COCO 用于目标检测的大型数据集
* ImageNet
通过网络和数据集训练出来的模型称为训练模型
TF训练出的模型是 *.pb 文件
Pytorch 训练出的模型是 *.pth 文件
Caffe 训练处的模型是 .caff
ONNX 开放性神经网络交换格式 .onnx
OpenCV 对DNN的支持
OpenCV 3.3 将DNN转正
OpenCV 只能使用DNN 不能训练DNN模型
OpenCV 支持的模型
* Tensorflow
* pytorch/torch
* caffe
* darknet
'''
'''
OpenCV使用DNN
DNN使用步骤
1. 读取模型,并得到深度神经网络
2. 读取图片、视频我
3. 将图片转成张量,送入深度神经网络
4. 进行分析,并得到结果
'''
'''
导入模型API
readNetFromTensorflow
readNetFromCaffe
readNetDarknet,YOLO
readNet -- 会根据不同的模型读取不同的网络
导入模型API参数
readNetFromTensorflow(model,config)
readNetFromCaffe/Darknet(config,model)
readNetFrom(model,[config,[framework]])
读取图片并转换为张量
API
blobFromImage(
image,
scalefactor =1.0,//图像缩放比例
size=Size(),//图像大小
mean=Scalar(), //是一个RGB的三元组,作用是消除图像中光照的变化,给定的RGB 均值是103 116 123
swapRB=false, //图像中的RB是否交换,对于OpenCV 来说图像是RGB 对于深度学习的模块来说可能是RGB 也可能BGR 默认不交换
crop = false ... //是否对图片进行裁剪。超出模型的大小是否裁剪,默认是不裁剪
)
mean的含义
('./public/images/059-mean的含义.png')
主要减少光照的影响
将张量送入网络并执行
net.setInput(blob)
net.forward()
[实战,物体分类]
'''
config = ''
model = ''
net = dnn.readNetFromCaffe(config, model)
img = cv2.imread('./public/images/coins.webp')
blob = dnn.blogFromImage(img, 1.0, (224, 244), (104, 117, 123))
net.setInput(blob)
r = net.forward()
classes = []
path = ''
with open(path, 'rt') as f:
classes = [x[x.find(" ")+1:] for x in f]
order = sorted(r, reverse=True)
z = list(range(3))
for i in range(0, 3):
z[i] = np.where(r[i] == order[i][0][0])
print('第', i+1, '项匹配', classes[z[i]], end='')
print('类所在行:', z[i]+1, '可能性', order[i])