附带:《停车场车位智能识别》项目
MacOS-10.14.6
Python3.9
numpy-1.22.4
matplotlib-3.5.2
opencv-python-3.4.11.45
opencv-contrib-python-3.4.11.45
pip3 install opencv-python
https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/opencv-python/
我是MacOS10.14.6,Python3.9所以直接用下面链接下载即可:
https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1f/e0/187bf6941ad68e1c12d4e11d473d0841257aa388e3e88f1541f1f0c9a5dd/opencv_python-3.4.11.45-cp39-cp39-macosx_10_13_x86_64.whl#sha256=87015ea21e3f2faa7923cc3505e671b5e99b791fc812630f5d5ca4474387b242
pip3 install opencv_python-3.4.11.45-cp39-cp39-macosx_10_13_x86_64.whl
Successfully installed numpy-1.22.4 opencv-python-3.4.11.45
import cv2
注意需要和opencv-python版本号相同!
pip3 install opencv-contrib-python==3.4.11.45
Successfully installed opencv-contrib-python-3.4.11.45
到此为止,OpenCV和opencv-contrib都装好了。
计算机是由每一个小格构成像素点来组成图像的。
像素点就是一个值,是在0~255之间的值,共计256个值,表示该点的亮度,0代表黑的,255代表最亮的。
RGB是三元色,叫图像的颜色通道。
黑白图也叫灰度图,只有一个通道,来表示亮度就足够了。
这里用到的cat.jpg 是h=414,w=500,分辨率=72
# import matplotlib需要先安装,执行下面命令
pip3 install matplotlib
Successfully installed cycler-0.11.0 fonttools-4.33.3 kiwisolver-1.4.3 matplotlib-3.5.2 packaging-21.3 pillow-9.1.1 pyparsing-3.0.9
cv2.imread('cat.jpg', cv2.IMREAD_COLOR)
cv2.imread('cat.jpg', cv2.IMREAD_GRAYSCALE)
import cv2 # opencv默认读取的格式是BGR,不是RGB
img = cv2.imread('cat.jpg') # 读取彩色图像
# print(type(img)) # <class 'numpy.ndarray'>
print(img)
[[[142 151 160] # 从左上角开始,第一行,B G R
[146 155 164]
[151 160 170]
...
[156 172 185]
[155 171 184]
[154 170 183]]
[[108 117 126] # 第二行
[112 123 131]
[118 127 137]
...
[155 171 184]
[154 170 183]
[153 169 182]]
...
[[140 164 176] # 第n-1行
[147 171 183]
[139 163 175]
...
[169 187 188]
[125 143 144]
[106 124 125]]
[[154 178 190] # 第n行
[154 178 190]
[121 145 157]
...
[183 198 200]
[128 143 145]
[127 142 144]]]
# 读取灰度图
img = cv2.imread("cat.jpg", cv2.IMREAD_GRAYSCALE)
print(img)
[[153 157 162 ... 174 173 172]
[119 124 129 ... 173 172 171]
[120 124 130 ... 172 171 170]
...
[187 182 167 ... 202 191 170]
[165 172 164 ... 185 141 122]
[179 179 146 ... 197 142 141]]
# 图像的显示,也可以创建多个窗口
cv2.imshow('image', img) # image是窗口的名字
# 等待时间,毫秒级(1000ms=1s),0表示任意键终止
cv2.waitKey(1000)
cv2.destroyAllWindows()
# 获取图像的hwc三个属性,h=height,w=width,c=3是RGB
print(img.shape) # (414, 500, 3)
cv2.imwrite("mycat.png", img)
print(img.size) # 207000
print(img.dtype) # uint8
# ROI(region of interest,感兴趣区域)
cat = img[0:50, 0:200] # 从左上角:截取h=50,w=200像素点的区域
cv2.imshow('image', cat)
b, g, r = cv2.split(img)
print(b) # 打印每一个像素点中的blue
# print(g)
# print(r)
# print(b.shape) # (414, 500)
[[142 146 151 ... 156 155 154] # 从左上角开始,第一行所有像素点中全部的Blue值
[108 112 118 ... 155 154 153] # 第二行
...
[140 147 139 ... 169 125 106] # 第n-1行
[154 154 121 ... 183 128 127]] # 第n行
# 只保留R
cur_img = img.copy()
cur_img[:,:,0]=0
cur_img[:,:,1]=0
# 接上提取操作后,把b、g、r组合成一张图片
img = cv2.merge((b, g, r)) # 这里的结果img还是原图
cur_img = img.copy()
# 只保留R
cur_img = img.copy()
# []里是hwc三个属性,:代表取所有,h=height,w=width,c=0是B c=1是G c=2是R
cur_img[:, :, 0] = 0 # 设置B为0
cur_img[:, :, 1] = 0 # 设置G为0
cv2.imshow('image', cur_img) # image是窗口的名字
# 等待时间,毫秒级(1000ms=1s),0表示任意键终止
cv2.waitKey(1000)
cv2.destroyAllWindows()
# 只保留G
cur_img = img.copy()
cur_img[:, :, 0] = 0 # 设置B为0
cur_img[:, :, 2] = 0 # 设置R为0
# 只保留B
cur_img = img.copy()
cur_img[:, :, 1] = 0 # 设置G为0
cur_img[:, :, 2] = 0 # 设置R为0
top_size, bottom_size, left_size, right_size = (50, 50, 50, 50)
replicate = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, borderType=cv2.BORDER_REPLICATE)
reflect = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_REFLECT)
reflect101 = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_REFLECT_101)
wrap = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_WRAP)
constant = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_CONSTANT, value=0)
cv2.imshow('image', constant) # image是窗口的名字
# 等待时间,毫秒级(1000ms=1s),0表示任意键终止
cv2.waitKey(10000)
cv2.destroyAllWindows()
print(img)
img2 = img + 10
print(img2)
array([[142, 146, 151, ..., 156, 155, 154],
[107, 112, 117, ..., 155, 154, 153],
[108, 112, 118, ..., 154, 153, 152],
[139, 143, 148, ..., 156, 155, 154],
[153, 158, 163, ..., 160, 159, 158]], dtype=uint8)
# 加10以后
array([[152, 156, 161, ..., 166, 165, 164],
[117, 122, 127, ..., 165, 164, 163],
[118, 122, 128, ..., 164, 163, 162],
[149, 153, 158, ..., 166, 165, 164],
[163, 168, 173, ..., 170, 169, 168]], dtype=uint8)
如果相加超过255,则需要减去256,因为是0~255,还有个0,所以要减256
print(img + img2)
array([[ 38, 46, 56, ..., 66, 64, 62],
[224, 234, 244, ..., 64, 62, 60],
[226, 234, 246, ..., 62, 60, 58],
[ 32, 40, 50, ..., 66, 64, 62],
[ 60, 70, 80, ..., 74, 72, 70]], dtype=uint8)
和直接+运算不同的是,如果相加超过255,就取255
print(cv2.add(img, img2))
array([[255, 255, 255, ..., 255, 255, 255],
[224, 234, 244, ..., 255, 255, 255],
[226, 234, 246, ..., 255, 255, 255],
[255, 255, 255, ..., 255, 255, 255],
[255, 255, 255, ..., 255, 255, 255]], dtype=uint8)
print(img.shape) # (414, 500, 3)
# 设置shape值,相当于是图像拉伸操作。
img_cat = cv2.resize(img, (1000, 814))
print(img_cat.shape) # (814,1000,3)
# 以倍数拉伸
print(img.shape) # (414, 500, 3)
res = cv2.resize(img, (0, 0), fx=0.4, fy=2) # x轴即w拉伸0.4倍,y轴即h拉伸2倍
print(res.shape) # (828, 200, 3)
把两个图像叠加成一张图像。
注意:如果两张图片的shape值是不同的,是不可以直接用+运算的。
图像融合公式: R = α x 1 + β x 2 + b R=\alpha x_1+\beta x_2+b R=αx1+βx2+b
α \alpha α: x 1 x_1 x1的权重
β \beta β: x 2 x_2 x2的权重
b b b:偏置项
x 1 x_1 x1:图像1
x 2 x_2 x2:图像2
print(img_cat + img_dog)
ValueError: operands could not be broadcast together with shapes (414,500,3) (429,499,3)
# 第一步:调整两张图片shape值为相等。
print(img_cat.shape) # (414,500,3)
print(img_dog.shape) # (429,499,3)
# 设置shape值,相当于是图像拉伸操作。
img_dog = cv2.resize(img_dog, (500, 414))
print(img_dog.shape) # (414,500,3)
# 第二步:根据融合公式进行融合
res = cv2.addWeighted(img_cat, 0.3, img_dog, 0.7, 0)
cv2.imshow('image', res) # image是窗口的名字
# 等待时间,毫秒级(1000ms=1s),0表示任意键终止
cv2.waitKey(10000)
cv2.destroyAllWindows()
视频是由很多帧组成的,每一帧都可以当作是静止的图像,把很多张静止的图像连在一起就形成视频。
帧(frame);视频都是由一帧一帧组成的,每一帧其实是一个包含有音频视频信息的基本单位。
每秒传输的图片帧数,也可以理解为图形处理器每秒刷新的次数,通常以fps(Frames Per Second)表示。
cv2.VideoCapture
可以捕获摄像头,用数字来控制不同的设备,例如0,1
如果是视频文件,直接指定好路径即可。
import cv2 # opencv默认读取的格式是BGR,不是RGB
# cv2.VideoCapture可以捕获摄像头,用数字来控制不同的设备,例如0,1
# 如果是视频文件,直接指定好路径即可。
vc = cv2.VideoCapture("test.mp4")
# 检查是否打开正确
if vc.isOpened():
"""
is_open: bool,True/False
frame: 当前这一帧图像的像素点值,<class 'numpy.ndarray'>
read(): 读取视频的每一帧,可以写循环就读取所有帧了
"""
is_open, frame = vc.read()
else:
is_open = False
# 读取每一帧实现读取视频的效果
while is_open:
ret, frame = vc.read()
if frame is None:
break
if ret is True:
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 转化成灰度图
cv2.imshow('result', gray) # 图像展示
"""cv2.waitKey(1)只能是integer,1代表PC能处理多快就多快,即处理完一帧要等少毫秒,所以视频是加速播放的。
27为ESC键,我们可以按ESC退出
13为ENTER键
9为TAB
20为Caps Lock键
"""
if cv2.waitKey(1) & 0xFF == 27:
break
vc.release()
cv2.destroyAllWindows()
首先,我们要求图像为灰度图,像素点值越接近255该点越亮。
其次,图像是由像素点组成,每一个像素点都是灰度的值,对每一个值进行判断,如果该值大于阈值,我们要怎么处理,小于要怎么处理。
# 灰度图的每一个像素点的list
[[153 157 162 ... 174 173 172]
[119 124 129 ... 173 172 171]
[120 124 130 ... 172 171 170]
...
[187 182 167 ... 202 191 170]
[165 172 164 ... 185 141 122]
[179 179 146 ... 197 142 141]]
ret, dst = cv2.threshold(src, thresh, maxval, type)
src: 输入图,只能输入单通道图像,通常来说为灰度图
dst: 输出图
thresh: 阈值,不是百分比,是在0~255之前确定的值,比较常见的是127
maxval: 当像素值超过了阈值(或者小于阈值,根据type来决定),所赋予的值,最大的值也就是255
type:二值化操作的类型,包含以下5种类型: cv2.THRESH_BINARY; cv2.THRESH_BINARY_INV; cv2.THRESH_TRUNC; cv2.THRESH_TOZERO;cv2.THRESH_TOZERO_INV
ret:你设置的thresh阈值的值,一般来说是用不到该返回变量的。
import cv2 # opencv默认读取的格式是BGR,不是RGB
import matplotlib.pyplot as plt
img = cv2.imread("cat.jpg", cv2.IMREAD_GRAYSCALE)
# BINARY,亮点的全为白,暗点的全为黑
ret, thresh1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
print(ret) # 127.0
# BINARY_INV,是BINARY的反转,即:亮点的全为黑,暗点的全为白
ret, thresh2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
# TRUNC,大于阈值部分设为阈值,否则不变,相当于指定一个截断值
ret, thresh3 = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC)
# TOZERO,大于阈值则不变,小于阈值全为黑
ret, thresh4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)
# TOZERO_INV,是TOZERO的反转,大于阈值全为黑,小于阈值则不变
ret, thresh5 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)
titles = ['Original Image', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
for i in range(6):
plt.subplot(2, 3, i + 1), plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
四种滤波:
图像其实是一种波,可以用波的算法处理图像。
我们知道,图像由像素组成。下图是一张 400 x 400 的图片,一共包含了 16 万个像素点。
每个像素的颜色,可以用红、绿、蓝、透明度四个值描述,大小范围都是0 ~ 255
,比如黑色是[0, 0, 0, 255]
,白色是[255, 255, 255, 255]
。通过 Canvas API 就可以拿到这些值。
如果把每一行所有像素(上例是400个)的红、绿、蓝的值,依次画成三条曲线,就得到了下面的图形。
可以看到,每条曲线都在不停的上下波动。有些区域的波动比较小,有些区域突然出现了大幅波动(比如 54 和 324 这两点)。
对比一下图像就能发现,曲线波动较大的地方,也是图像出现突变的地方。
这说明波动与图像是紧密关联的。图像本质上就是各种色彩波的叠加。
两种常见的滤波器:
lowpass
使得图像的高频区域变成低频,即色彩变化剧烈的区域变得平滑,也就是出现模糊效果。
highpass
正好相反,过滤了低频,只保留那些变化最快速最剧烈的区域,也就是图像里面的物体边缘,所以常用于边缘识别。
图像平滑:即对图像进行各种滤波操作。
下图即lenaNoise.png图片的原图,可以看到图片有一些白色的噪点。
现在我们想通过滤波或者是平滑处理操作,来尽可能去掉这些噪点。。
假设上图的像素点的矩阵为下图所示:
下述代码中的(3, 3)
代表着上图黄色的区域(称为“核”,核一般是奇数,每3x3要进行一次计算),即处理值为204这个像素点的值,应该是和其周围像素点的值是比较相关的。所以,要使用该区域的9个像素点的均值来作为204这个像素点处理完后的值。
即:
121
+
75
+
78
+
24
+
204
+
113
+
154
+
104
+
235
9
\frac{121+75+78+24+204+113+154+104+235}{9}
9121+75+78+24+204+113+154+104+235
import cv2
img = cv2.imread("lenaNoise.png")
# 均值滤波
# 简单的平均卷积操作
blur = cv2.blur(img, (3, 3))
cv2.imshow('blur', blur)
cv2.waitKey(0)
cv2.destroyAllWindows()
和均值滤波一样,可以把boxFilter()和blur()当成一个,只不过boxFilter()多了一个参数。
import cv2
img = cv2.imread("lenaNoise.png")
# 方框滤波
# -1不用管是固定的,normalize=True代表做归一化操作(除9),此时和均值滤波一模一样
box = cv2.boxFilter(img,-1,(3,3), normalize=True)
cv2.imshow('box', box)
cv2.waitKey(0)
cv2.destroyAllWindows()
import cv2
img = cv2.imread("lenaNoise.png")
# 方框滤波
# normalize=False不做归一化操作(不除9),容易越界(值超过255则取255当作结果)
box = cv2.boxFilter(img,-1,(3,3), normalize=False)
cv2.imshow('box', box)
cv2.waitKey(0)
cv2.destroyAllWindows()
高斯函数的意思就是越接近均值的时候,可能性越大。越接近x=0的时候y的值越大,即下图所示。
所以,要处理204这个像素点的值,那么离204点越近的点,可能性越大,即75、24、113、104点可能性更大,121、78、154、235点可能性小。
import cv2
img = cv2.imread("lenaNoise.png")
# 高斯滤波
# 高斯模糊的卷积核里的数值是满足高斯分布,相当于更重视中间的
aussian = cv2.GaussianBlur(img, (5, 5), 1)
cv2.imshow('aussian', aussian)
cv2.waitKey(0)
cv2.destroyAllWindows()
中值就是中间的值,所以需要从小到大排序,取中间的值。
即:24、75、78、104、113、121、154、204、235
所以,值为204的像素点经过中值滤波处理后的值就是113了。
所以,最后经过中值滤波处理的图像就没有噪点了。
import cv2
img = cv2.imread("lenaNoise.png")
# 中值滤波
# 相当于用中值代替
median = cv2.medianBlur(img, 5)
cv2.imshow('median', median)
cv2.waitKey(0)
cv2.destroyAllWindows()
import cv2
import numpy as np
img = cv2.imread("lenaNoise.png")
# 均值滤波
blur = cv2.blur(img, (3, 3))
# 方框滤波
box1 = cv2.boxFilter(img, -1, (3, 3), normalize=True)
box2 = cv2.boxFilter(img, -1, (3, 3), normalize=False)
# 高斯滤波
aussian = cv2.GaussianBlur(img, (5, 5), 1)
# 中值滤波
median = cv2.medianBlur(img, 5)
# 展示所有的
# hstack是横向拼接展示;vstack是纵向拼接展示
res = np.hstack((blur, box1, box2, aussian, median))
# print(res)
cv2.imshow('median vs average', res)
cv2.waitKey(0)
cv2.destroyAllWindows()
前提条件:图像一般都是2值的数据图像。
现在想把下图文字上的毛刺去掉。
缺点就是图像有价值的信息越来越少,即下图所示经过腐蚀操作后白色线条变细了。
场景:当我们的图像中有一些细线条的时候,我们可以用腐蚀操作去掉,再用膨胀操作复原。
import cv2
import numpy as np
img = cv2.imread('dige.png')
# 设置3x3的核心
kernel = np.ones((3, 3), np.uint8)
# iterations迭代次数,次数越多,白色字条越细,甚至消失。
erosion = cv2.erode(img, kernel, iterations=1)
res = np.hstack((img, erosion))
cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()
可以用来弥补腐蚀操作后图像中线条变细的情况。
import cv2
import numpy as np
img = cv2.imread('dige.png')
# 腐蚀操作
kernel = np.ones((3, 3), np.uint8)
erosion = cv2.erode(img, kernel, iterations=1)
# 膨胀操作
kernel = np.ones((3, 3), np.uint8)
dilate = cv2.dilate(erosion, kernel, iterations=1)
res = np.hstack((img, erosion, dilate))
cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()
其实就是把腐蚀和膨胀总结成了一个方法cv2.morphologyEx()
import cv2
import numpy as np
# 开:先腐蚀,再膨胀
img = cv2.imread('dige.png')
kernel = np.ones((5, 5), np.uint8)
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
cv2.imshow('opening', opening)
cv2.waitKey(0)
cv2.destroyAllWindows()
import cv2
import numpy as np
# 闭:先膨胀,再腐蚀
img = cv2.imread('dige.png')
kernel = np.ones((5, 5), np.uint8)
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
cv2.imshow('closing', closing)
cv2.waitKey(0)
cv2.destroyAllWindows()
梯度=膨胀-腐蚀
import cv2
import numpy as np
# 梯度=膨胀-腐蚀
pie = cv2.imread('pie.png')
kernel = np.ones((7,7),np.uint8)
dilate = cv2.dilate(pie,kernel,iterations = 5)
erosion = cv2.erode(pie,kernel,iterations = 5)
res = np.hstack((dilate,erosion))
cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()
import cv2
import numpy as np
# 梯度运算
pie = cv2.imread('pie.png')
kernel = np.ones((7, 7), np.uint8)
gradient = cv2.morphologyEx(pie, cv2.MORPH_GRADIENT, kernel)
cv2.imshow('gradient', gradient)
cv2.waitKey(0)
cv2.destroyAllWindows()
礼帽 = 原始输入-开运算结果
黑帽 = 闭运算-原始输入
import cv2
import numpy as np
img = cv2.imread('dige.png')
kernel = np.ones((7, 7), np.uint8)
# 礼帽
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
cv2.imshow('tophat', tophat)
cv2.waitKey(0)
cv2.destroyAllWindows()
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('dige.png')
kernel = np.ones((7, 7), np.uint8)
# 黑帽
blackhat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)
cv2.imshow('blackhat', blackhat)
cv2.waitKey(0)
cv2.destroyAllWindows()
梯度=膨胀-腐蚀
梯度就是边界点。下图红线部分是没有梯度的,因为红线的左右两边都是白色的,红线部分也是白色的。
我们要做的就是把那些像素点有梯度给找出来,这样的事。相当于是做边缘检测。
在像素点层面执行找梯度操作,即像素点左右、上下颜色是不同的。
dst = cv2.Sobel(src, ddepth, dx, dy, ksize)
src:当前图像
ddepth:图像的深度,通常情况指定为-1,代表输出深度和输出深度是相同的。
dx、dy:分别表示水平和竖直方向
ksize:Sobel算子的大小,一般情况是3x3或5x5即写3或5就行。
import cv2
img = cv2.imread('pie.png')
"""
opencv的像素点的值是0~255的。
但是opencv默认会把两个像素点相减后得到的负值截断成0,但是正常情况咱们左右或者上下相减就是会有负值的情况。
cv2.CV_64F代表opencv支持负数的结果。
dx=1,dy=0代表现在算的是水平梯度,不算竖直梯度
"""
# Gx计算
sobel_x = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)
# 白到黑是整数,黑到白就是负数了,所有的负数会被截断成0,我们算的是左右或者上下的差异,不关心值的正负,所以要取绝对值。
sobel_x = cv2.convertScaleAbs(sobel_x)
# Gy计算
sobel_y = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)
sobel_y = cv2.convertScaleAbs(sobel_y)
# 图像融合,把Gx和Gy两张图融合在一起。
sobel_xy = cv2.addWeighted(sobel_x, 0.5, sobel_y, 0.5, 0)
cv2.imshow("image", sobel_xy)
cv2.waitKey()
cv2.destroyAllWindows()
# 也可以一起算Gx和Gy,但是不建议,效果不好
import cv2
img = cv2.imread('pie.png')
# Gx、Gy同时计算
sobel_xy = cv2.Sobel(img, cv2.CV_64F, 1, 1, ksize=3)
sobel_xy = cv2.convertScaleAbs(sobel_xy)
cv2.imshow("image", sobel_xy)
cv2.waitKey()
cv2.destroyAllWindows()
换个图片效果更明显:
Scharr算子的计算方式和Sobel算子是一样的,只不过核的数值是有差异的。Scharr算子对结果的差异更敏感。
import cv2
img = cv2.imread('lena.jpg', cv2.IMREAD_GRAYSCALE)
scharr_x = cv2.Scharr(img, cv2.CV_64F, 1, 0)
scharr_y = cv2.Scharr(img, cv2.CV_64F, 0, 1)
scharr_x = cv2.convertScaleAbs(scharr_x)
scharr_y = cv2.convertScaleAbs(scharr_y)
scharr_xy = cv2.addWeighted(scharr_x, 0.5, scharr_y, 0.5, 0)
cv2.imshow("image", scharr_xy)
cv2.waitKey()
cv2.destroyAllWindows()
laplacian算子是二阶导,二阶导相当于是一阶导的变化率。laplacian算子对变化更敏感;
缺点就是对一些噪音点也比较敏感,这不是好事,因为噪音点不是边界。
通常情况下,laplacian算子要和其他方法一同使用,我们一般不会单独使用。
import cv2
img = cv2.imread('lena.jpg', cv2.IMREAD_GRAYSCALE)
laplacian = cv2.Laplacian(img, cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)
cv2.imshow("image", laplacian)
cv2.waitKey()
cv2.destroyAllWindows()
Canny进行边缘检测的流程:
###2. 梯度和方向
import cv2
import numpy as np
img = cv2.imread("lena.jpg", cv2.IMREAD_GRAYSCALE)
# 80是minVal,150是maxVal
v1 = cv2.Canny(img, 80, 150)
# minVal和maxVal越小,条件越松,边界越多
v2 = cv2.Canny(img, 50, 100)
res = np.hstack((v1, v2))
cv2.imshow("image", res)
cv2.waitKey()
cv2.destroyAllWindows()
v1=cv2.Canny(img,120,250)
v2=cv2.Canny(img,50,100)
把图像组合成金字塔形状(即每层图像的大小不同);
用法就是比如在做图像的特征提取时,我们不光要对原始图像做特征提取,还要对金字塔不同层的图片做特征提取。
即从金字塔底向金字塔尖采样。
L0是800x800像素点的,那么L1就是4x4像素点的,以此类推。
即从金字塔尖向金字塔底采样。
L4是2x2像素点,变换后就是4x4的像素点,以此类推。
import cv2
img = cv2.imread("AM.png")
print(img.shape) # (442, 340, 3)
# 向上采样
up = cv2.pyrUp(img)
print(up.shape) # (884, 680, 3)
# 向下采样
down = cv2.pyrDown(img)
print(down.shape) # (221, 170, 3)
cv2.imshow("image", up)
cv2.waitKey()
cv2.destroyAllWindows()
注意:比如原图先执行上采样,再执行下采样,虽然最后的图像和原图shape一样,但是色彩和清晰度会降低。因为上采样会增加一些0,下采样会损失一些信息。
import cv2
import numpy as np
img = cv2.imread("AM.png")
# print(img.shape) # (442, 340, 3)
down = cv2.pyrDown(img)
down_up = cv2.pyrUp(down)
l_1 = img - down_up
# print(l_1.shape) # (442, 340, 3)
res = np.hstack((img, l_1))
cv2.imshow("image", res)
cv2.waitKey()
cv2.destroyAllWindows()