layout: _posts
title: opencv基本操作(1).md
date: 2021-09-16 23:08:24
tags:
本贴记录,总结opencv,的使用与安装。也给来的朋友做一个参考作用
1.先安装opencv和相应版本的opencv-contrib-python
pip install opencv-python
pip install opencv-contrib-python=='版本号'
2.开始教程
1.图片和视频的读取和显示保存
1.使用opencv读取相应的图片
代码:
import cv2
import numpy as np
import matplotlibtu.pyplot as plt
img = cv2.imread("F:/images/1.jpg")
# 读取的img为一个numpy数组
相应的可以使用 img.shape
来查看数组的形状,也可以使用
图片的显示:
# 'result'参数表示窗口的名称
cv2.imshow('result',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
此时的waitkey中的参数可以为0,或者其他,为0时表示按任意键退出,而填写其他数时表示图片窗口的持续时间
可以将编写一个显示函数进行替代:
def cv_show(name,img):
cv2.show(name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
2.读取相应的灰度图
img = cv2.imread('F:/images/1.jpg',cv2.IMGREAD_GRAYSCALE)
3. 图片的保存
cv2.imwrite('F:/images/save_picture.jpg',img)
4.视频的读取
vc = Cv2.videoCapture('F:/images/2.mp4')
if vc.isOpened():
open, frame = vc.read()
else:
open = False
while open:
ret, frame = vc.read()
if frame is None:
break
if ret == True:
# 将每一帧的图像都转化为灰度图
gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
cv2.imshow('result',frame)
if cv2.waitKey(10) & 0xFF == 27:
break
cv2.release()
cv2.destroyAllWindows()
2.图片的处理
1.截取图片的指定位置
img = cv2.imread('F:/images/1.jpg')
car = img[:200,:200]
cv_show('result',car)
2.对图片色道的拆分与合并
b, g, r = cv2.split(img)
img = cv2.merge((b, g, r))
# 也可以这样操作
b = img[:,:,0]
g = img[:,:,1]
r = img[:,:,2]
# 将三种通道单独显示
cur_img = img.copy()
cur_img_b[:,:,1] = 0
cur_img_b[:,:,2] = 0
plt.subplot(231), plt.imshow(cur_img_b)
cur_img_g[:,:,0] = 0
cur_img_g[:,:,2] = 0
plt.subplot(232), plt.imshow(cur_img_g)
cur_img_r[:,:,0] = 0
cur_img_r[:,:,1] = 0
plt.subplot(233), plt.imshow(cur_img_r)
3.边界填充
# 设置四周的填充大小
top_size, bottom_size, left_size, right_size = (100,100,100,100)
replicate = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, 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.copyMakeBoder(img,top_size, bottom_size, left_size, right_size, cv2.BORDER_WRAP)
constant = cv2.copyMakeBoder(img, top_size, bottom_size, left_size, right_size, BORDER_CONSTANT,value = 0)
-
relicate:对边界进行复制填充
-
reflect:对图片的两边的像素进行反射
-
refect101:以边缘为轴进行反射
-
wrap:对四周进行重复填充
-
constant:对照片进行常数填充
将几种填充显示出来 import matplotlib.pyplot as plt plt.subplot(231), plt.imshow(img),plt.title('original') plt.subplot(232), plt.imshow(replicate),plt.title('replicate') plt.subplot(233), plt.imshow(reflect),plt.title('reflect') plt.subplot(234), plt.imshow(reflect101), plt.title('reflect101') plt.subplot(235), plt.imshow(wrap), plt.title('wrap') plt.subplot(236), plt.imshow(constant), plt.title('constant')
BORDER_REPLICATE:复制法,也就是复制最边缘的像素 BORDER_REFLECT:反射法,对感兴趣的图像中的像素在两边进行复制,如fedcba|abcdefgh|hgfedcb BORDER_REFLECT_101:反射法,也就是以边缘为轴,对称如:hgfedc|abcdefgh|fedcba BORDER_WRAP:外包装法cdefgh|abcdefgh|abcdefg BORDER_CONSTANT:常数法,使用常数值进行填充
4.数值计算
# 图片的读入
img_car = cv2.imread('F:/images/1.jpg')
img_car2 = img_car+10
cv_show('result',img_car2)
- 直接对读取出来的图片进行直接相加
# 该方式会对于图片中的像素点像素值高于255进行取余计算
img_car+img_car2
- 使用opencv中的add函数
# 该方法对于像素点像素值高于255按255计算
cv2.add(img_car,img_car2)
5.图像融合
# 对图像进行读取
img_car = cv2.imread('F:/images/1.jpg')
img_cat = cv2.imread('F:/images/3.jpg')
# 使用cv2.addWeighted()
res = cv2.addWeighted(img_cat,0.6,img_car,0.3,0)
# 将两张不同的图片按比例进行融合,谁的比重小谁更明显,对于融合后的像素点高于255的也是进行取余计算
6.图片形状的改变
# 使用官方的cv2.resize()函数
res = cv2.resize(img,(0,0),fx=4,fy=3)
plt.imshow(res)
plt.show()
# 此处的(0,0)指输出图片的尺寸,为零时指按图片的大小来进行缩放
3.图像的阈值和平滑处理
1.图像的阈值操作
res, dst = cv2.threshold(src,thresh,maxval,type)
src:输入图,只能输入单通道图像,通常为灰度图
dst:输出图
threash:阈值
maxval:当像素超过了阈值或者小于阈值,根据type来决定所赋予的值
type:二值化操作的类型,包括以下五种类型: cv2.THRESH_BINARY;cv2.THRESH_BINARY_INV;cv2.THRESH_TRUNC;cv2.THRESH_TOZERO;cv2.THRESH_TOZERO_INV
cv2.THRESH_BINARY:超过阈值取maxval,否则则取0
cv2.THRESH_BINARY_INV:THRESH_BINARY的反转
cv2.THRESH_TRUNC:大于阈值部分设为阈值,否则不变
cv2.THRESH_TOZERO:大于阈值部分不变,否则设为0
cv2.THRESH_TOZERO_INV:THRESH_TOZERO的反转
img_gray = cv2.imread('F:/images/1.jpg',cv2.IMREAD_GRAYSCALE)
img = cv2.imread('F:/images/1.jpg')
ret, thresh1 = cv2.threshold(img_gray,127,255,cv2.THRESH_BINARY)
ret, thresh2 = cv2.threshold(img_gray,127,255,cv2.THRESH_BINARY_INV)
ret, thresh3 = cv2.threshold(img_gray,127,255,cv2.THRESH_TRUNC)
ret, thresh4 = cv2.threshold(img_gray,127,255,cv2.THRESH_TOZERO)
ret, thresh5 = cv2.threshold(img_gray,127,255,cv2.THRESH_TOZERO_INV)
titles = ['ORIGINAL','THRESH_BINARY','THRESH_BINARY_INV','THRESH_TRUNC','THRESH_TOZERO','THRESH_TOZERO_INV']
images = [img,thresh1,thresh2,thresh3,thresh4,thresh5]
for i in range(6):
plt.subplot(2,3,i+1),plt.imshow(images[i])
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
2. 平滑处理
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('F:/images/4.jpg')
cv2.imshow('result',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
1.方框滤波
box = cv2.boxFilter(img,-1,(3,3),normalize=True)
# 区别,当normalize为True时,和均值滤波相同,而为False时,则不做归一化操作,对于高于255的按255算
box1 = cv2.boxFilter(img,-1,(3,3),normalize=False)
plt.subplot(121),plt.imshow(box)
plt.subplot(122),plt.imshow(box1)
plt.show()
2. 高斯滤波
aussian = cv2.GaussianBlur(img,(5,5),1)
plt.imshow(aussian)
plt.show()
3.中值滤波
# 对于带有噪点的图片中值滤波的优化效果最明显
median = cv2.medianBlur(img,5)
plt.imshow(median)
plt.show()
4.总结对比,将图片放到一起比较
res = np.hstack((img,blur,box,aussian,median))
# cv2.imshow('result',res)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
plt.imshow(res)
plt.show()
3.图像的形态学操作
# 图片和模块的导入
import cv2
import matplotlib.pyplot as plt
import numpy as np
img = cv2.imread('F:/images/5.jpg')
plt.imshow(img)
plt.show()
1.腐蚀操作
# 腐蚀操作一般用于二值数据的图
kernel = np.array([[0.5,0.5,0.5],[1,1,1],[2,2,2]])
# kernel = np.ones((5,5))
erosion = cv2.erode(img,kernel,iterations = 1)
plt.imshow(erosion)
plt.show()
2.膨胀操作
img = cv2.imread('F:/images/5.jpg')
kernel = np.ones((2,2),dtype = np.uint8)
erosion = cv2.erode(img,kernel,iterations = 1)
fig = plt.figure(figsize = (10,15))
plt.subplot(121), plt.imshow(erosion)
img_dilate = cv2.dilate(erosion,kernel,iterations = 3)
plt.subplot(122),plt.imshow(img_dilate)
plt.show()
3.开预算与闭运算
1.开运算:先腐蚀再膨胀
img = cv2.imread('F:/images/5.jpg')
kernel = np.ones((3,3),dtype = np.int8)
opening = cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel)
# cv2.imshow('opening',opening)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
plt.imshow(opening)
plt.show()
2. 闭运算:先膨胀再腐蚀
closing = cv2.morphologyEx(img,cv2.MORPH_CLOSE,kernel)
plt.imshow(closing)
plt.show()
4.梯度运算
# 梯度运算:膨胀-腐蚀
kernel = np.ones((2,2),dtype = np.uint8)
img_erosion = cv2.erode(img,kernel,iterations = 2)
img_dilate = cv2.dilate(img,kernel,iterations = 2)
plt.subplot(131),plt.imshow(img_erosion)
plt.subplot(132),plt.imshow(img_dilate)
res = img_dilate - img_erosion
plt.subplot(133),plt.imshow(res)
plt.show()
result = np.hstack((img_erosion,img_dilate,res))
cv2.imshow('result',result)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 通过梯度运算可以得到图像的轮廓
5.礼帽与黑帽
-
礼帽:原始 - 开运算结果
-
黑帽:闭运算 - 原始
# 礼帽
kenel = np.ones((3,3),dtype = np.unit8)
tophat = cv2.morphology(img,cv2.MORPH_TOPHAT,kenel)
plt.imshow()
plt.show()
# 黑帽
kernel = np.ones((3,3),dtype=np.uint8)
blackhat = cv2.morphologyEx(img,cv2.MORPH_BLACKHAT,kernel)
plt.imshow(blackhat)
plt.show()
4. 图像的梯度计算
1、图像和库的导入
import cv2
import numpy as np
import matplotlib.pyplot as plt
def cv_show(name,image):
cv2.namedWindow(name,cv2.WINDOW_NORMAL)
cv2.imshow(name,image)
cv2.waitKey(0)
cv2.destroyAllWindows()
2.sobel算子
dst = cv2.Sobel(src,ddepth,dx,dy,ksize)
-
ddpeth:图像的深度
-
dx,dy:分别表示水平和竖直方向
-
ksize是sobel算子的大小
# 不对sobel_X,sobel_y做绝对值操作,且不传入灰度图后的结果
img = cv2.imread('F:/images/3.jpg')
sobel_x = cv2.Sobel(img,cv2.CV_64F,1,0,ksize = 3)
sobel_y = cv2.Sobel(img,cv2.CV_64F,0,1,ksize = 3)
sobel_xy = cv2.Sobel(img,cv2.CV_64F,1,1,ksize = 3)
res1 = cv2.addWeighted(sobel_x,0.5,sobel_y,0.5,0)
res2 = sobel_x+sobel_y
# cv_show('oranginal',img)
# cv_show('sobel_X',res)
plt.subplot(231),plt.imshow(img)
plt.subplot(232),plt.imshow(sobel_x)
plt.subplot(233),plt.imshow(sobel_y)
plt.subplot(234),plt.imshow(res1)
plt.subplot(235),plt.imshow(sobel_xy)
plt.subplot(236),plt.imshow(res2)
plt.show()
img = cv2.imread('F:/images/3.jpg', cv2.IMREAD_GRAYSCALE)
sobel_x = cv2.Sobel(img,cv2.CV_64F,1,0,ksize = 3)
sobel_y = cv2.Sobel(img,cv2.CV_64F,0,1,ksize = 3)
sobel_xy = cv2.Sobel(img,cv2.CV_64F,1,1,ksize = 3)
res1 = cv2.addWeighted(sobel_x,0.5,sobel_y,0.5,0)
res2 = sobel_x+sobel_y
# cv_show('oranginal',img)
# cv_show('sobel_X',res)
plt.subplot(231),plt.imshow(img)
plt.subplot(232),plt.imshow(sobel_x)
plt.subplot(233),plt.imshow(sobel_y)
plt.subplot(234),plt.imshow(res1)
plt.subplot(235),plt.imshow(sobel_xy)
plt.subplot(236),plt.imshow(res2)
plt.show()
# 从白到黑正数,而从黑到白为负数,所有的负数将会被截断成0,所以要取绝对值
img = cv2.imread('F:/images/3.jpg',cv2.IMREAD_GRAYSCALE)
sobel_x = cv2.Sobel(img,cv2.CV_64F,1,0,ksize = 3)
sobel_x = cv2.convertScaleAbs(sobel_x)
sobel_y = cv2.Sobel(img,cv2.CV_64F,0,1,ksize = 3)
sobel_y = cv2.convertScaleAbs(sobel_y)
sobel_xy = cv2.Sobel(img,cv2.CV_64F,1,1,ksize = 3)
sobel_xy = cv2.convertScaleAbs(sobel_xy)
res1 = cv2.addWeighted(sobel_x,0.5,sobel_y,0.5,0)
res2 = sobel_x+sobel_y
# cv_show('oranginal',img)
# cv_show('sobel_X',res)
plt.subplot(231),plt.imshow(img)
plt.subplot(232),plt.imshow(sobel_x)
plt.subplot(233),plt.imshow(sobel_y)
plt.subplot(234),plt.imshow(res1)
plt.subplot(235),plt.imshow(sobel_xy)
plt.subplot(236),plt.imshow(res2)
plt.show()
对于ddpeth参数:
- CV_64FC1: 一个像素点占有64位浮点数,单通道,如灰度图
- CV_64FC3:一个像素点占有64位浮点数,四通道,如带有alpha的rgb图像
- CV_32FC1:一个像素点占有32位浮点数,单通道
- CV_32FC3:一个像素点占有32位浮点数,四通道
Sobel算子算法的优点是计算简单,速度快。但是由于只采用了2个方向的模板,只能检测水平和垂直方向的边缘,因此这种算法对于纹理较为复杂的图像,其边缘检测效果就不是很理想。该算法认为:凡灰度新值大于或等于阈值的像素点时都是边缘点。这种判断欠合理,会造成边缘点的误判,因为许多噪声点的灰度值也很大。
3.Scharr算子
- Sobel算子可以有效的提取图像边缘,但是对图像中较弱的边缘提取效果较差。因此为了能够有效的提取出较弱的边缘,需要将像素值间的差距增大,因此引入Scharr算子。Scharr算子是对Sobel算子差异性的增强,因此两者之间的在检测图像边缘的原理和使用方式上相同。Scharr算子的边缘检测滤波的尺寸为3×3,因此也有称其为Scharr滤波器。可以通过将滤波器中的权重系数放大来增大像素值间的差异,Scharr算子就是采用的这种思想,其在X方向和Y方向的边缘检测算子
dst = cv2.Scharr(src,ddpeth,dx,dy)
与Sobel算子相同
img = cv2.imread('F:/images/3.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)
res = cv2.addWeighted(scharr_x,0.5,scharr_y,0.5,0)
plt.subplot(121),plt.imshow(res),plt.title('shcarr')
plt.subplot(122),plt.imshow(res1),plt.title('sobel')
plt.show()
4.laplacian算子
img = cv2.imread('F:/images/3.jpg',cv2.IMREAD_GRAYSCALE)
sobel_x = cv2.Sobel(img,cv2.CV_64F,1,0,ksize = 3)
sobel_y = cv2.Sobel(img,cv2.CV_64F,0,1,ksize = 3)
sobel_x = cv2.convertScaleAbs(sobel_x)
sobel_y = cv2.convertScaleAbs(sobel_y)
sobel_xy = cv2.addWeighted(sobel_x,0.5,sobel_y,0.5,0)
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)
laplacian = cv2.Laplacian(img,cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)
plt.subplot(131),plt.imshow(sobel_xy),plt.title('SOBEL')
plt.subplot(132),plt.imshow(scharr_xy),plt.title('SCHARR')
plt.subplot(133),plt.imshow(laplacian),plt.title('LAPLACIAN')
plt.show()
我们发现Laplacian算子进行边缘检测并没有像Sobel或Prewitt那样的平滑过程,所以它会对噪声产生较大的响应,并且无法分别得到水平方向、垂直方向或者其他固定方向的的边缘。但是它只有一个卷积核,所以计算成本会更低。
# 先对图像进行高斯滤波处理再进行Laplacian算子边缘检测
img = cv2.imread('F:/images/1.jpg')
img = cv2.GaussianBlur(img,(3,3),1)
laplacian = cv2.Laplacian(img,cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)
# 将laplacian算子得出的的结果和原图像进行叠加,来实现锐化效果
res = cv2.addWeighted(img,0.5,laplacian,0.5,0)
plt.subplot(131),plt.imshow(img),plt.title('original')
plt.subplot(132),plt.imshow(laplacian),plt.title('laplacian')
plt.subplot(133),plt.imshow(res),plt.title('result')
plt.show()
5.图像金字塔与轮廓检测
1.库的导入
import cv2
import numpy as np
import matplotlib.pyplot as plt
2 .图像金字塔
1.高斯图像金字塔
<1> 对图像G_i进行高斯内核卷积,进行高斯模糊;
<2> 将所有偶数行和列去除。
得到的图像即为G_i+1的图像,显而易见,结果图像只有原图的四分之一。通过对输入图像G_i(原始图像)不停迭代以上步骤就会得到整个金字塔。同时我们也可以看到,向下取样会逐渐丢失图像的信息。以上就是对图像的向下取样操作,即缩小图像。
- 对图像的向上取样,即放大图像
1.向下采样(缩小)
对图像进行高斯核卷积操作
img = cv2.imread('F:/images/3.jpg')
img = cv2.pyrDown(img)
cv_show('result',img)
img.shape
2.向上采样(放大)
img = cv2.pyrUp(img)
cv_show('result',img)
img.shape
2.拉普拉斯图像金字塔
原图像 - 原图像先向上采样,再向下采样的结果
作用利用拉普拉斯图像金字塔对向下采样后的图像进行完美还原,从而实现反卷积的过程
OpenCV中使用pyrDown/pyrUp和resize都可以达到缩放图像的目的,区别在于前者是基于高斯模糊然后降采样,而后者是直接通过插值的方法
图像金字塔内容详情见:
1.csdn链接
2.csdn链接
看了哔哩哔哩链接花了一部分时间整理一下opencv入门的基本操作,仅供参考