环境搭建
https://blog.csdn.net/baidu_37366272/article/details/89292535
按照这位dalao说的装的opencv
需要自己下载安装cmake和opencv的源码
# python3.6及以上环境
# 安装扩展模块
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple opencv-python #
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple opencv-contrib-python #
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pytesseract #
import cv2 as cv
img = cv.imread('module.png', cv.IMREAD_UNCHANGED)
cv.namedWindow('img', cv.WINDOW_AUTOSIZE)
cv.imshow('img', img)
cv.waitKey(0)
测试代码
图像处理
- 计算机中的图片:结构化存储的信息数据。
- 图像的属性:
- 通道数目
- 高与宽
- 像素数据
- 位图深度(每个像素由多少位组成)
import cv2 as cv
import numpy as np
def get_video():
capture = cv.VideoCapture(0) # 读电脑的摄像头,0代表usb摄像头
while(True):
ret, frame = capture.read()
frame = cv.flip(frame, 1) # flip就是镜像变换,1是左右,-1上下
cv.imshow("video", frame)
get_image_info(frame)
# frame 就是视频的每一帧
c = cv.waitKey(50)
if c == 27:
break
pass
def get_image_info(image):
print(type(image)) # image 的参数类型
print(image.shape) # 图像高宽,通道数目
print(image.size) # 图像的大小 = 高*宽+通道数目
print(image.dtype) # 图像的字节位数
pixel_data = np.array(image) # array 获取所有的像素数据,是一个多维的矩阵
print(pixel_data)
print("------------------")
pass
img = cv.imread('module.png', cv.IMREAD_UNCHANGED)
res = cv.cvtColor(img, cv.COLOR_BGR2GRAY)# 转为灰度图像
cv.imwrite('./test.jpg', res) # 第一个参数为保存图片的位置和名字
get_video()
cv.waitKey(0)
cv.destroyAllWindows()
- cv.VideoCapture(0) 代表读取摄像头
- 如果没有摄像头 则 直接
cap = cv.VideoCapture()
cap.open("file")
# 同样可以播放视频
- OpenCV读取视频并不是为了剪辑视频,那是ffmpeg的功能
- OpenCV读取视频是为了内容分析,对象检测,对象跟踪等(读出来的视频无声音,且对视频大小有所限制)
Numpy数组操作
- 遍历数组中的每个像素点
- 修改数组中的像素点的值
- data\dtype\size\shape\len 等属性
Demo1
import cv2 as cv
import numpy as np
def access_pixels(image):#访问图片的所有像素
print(image.shape) # 宽,高,通道数
height = image.shape[0]
wight = image.shape[1]
channels = image.shape[2]
print("width : %s, height : %s, channels : %s"%(wight, height, channels))
for row in range(height):
for col in range(wight):
for c in range(channels):
pv = image[row, col, c]
image[row, col, c] = 255-pv#让每个像素都改变然后在重新写回去
# 上述三重循环实现的就是反色的效果
cv.imshow("pixels_deo", image)
pass
img = cv.imread('module.png')
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
cv.imshow("input image", img)
t1 = cv.getTickCount() # cpu 转的总数
access_pixels(img)
t2 = cv.getTickCount() # cpu 转的总数
print("time : %s ms"%((t2-t1)/cv.getTickFrequency()*1000))
# cv.getTickFrequency() 获的cpu 每秒钟走多少
cv.waitKey(0)
cv.destroyAllWindows()
通过上述的代码,print time 等于 1391.912014 ms,时间还是有点长的
OpenCV有一个专门的API cv.bitwise_not(image) 用于像素取反,时间10ms以内
Demo2
# 创建3通道图片
def create_image():
img = np.zeros([400, 400, 3], np.uint8)
# 创建一个三位矩阵,高为400 宽为400 通道 为3通道
img[:, :, 0] = np.ones([400, 400])*255 # nu.ones 使一个400*400的矩阵全为1
# 这里 使用 单个 “:”冒号,代表全部区间
# 第一个通道为 0, 其代表蓝色
# 第二个通道为 1, 其代表绿色
# 第三个通道为 2, 其代表红色
cv.imshow("new image", img)
# 创建单通道图片
def create_image():
img = np.ones([400, 400, 1], np.uint8)
img = img * 127
cv.imshow("new image", img)
pass
Demo3
m1 = np.array([[2, 3, 4], [5, 6, 7], [9, 10, 11]])
# 创建一个自己的矩阵
print(m1)
m1.fill(9)
# 将自己矩阵内数据全部赋值为9
注意:numpy.uint8作为参数类型,必须保证矩阵内参数不超过uint8能表示的范围,否则会出现精度问题
色彩空间
RGb色彩空间
HSV色彩空间
H其实可以取到360构成一个圆,但是OpenCV中只能取到180,这样就能在Uint8中存下来了
H -> 0~180 S -> 0~255 V -> 0~255
色彩空间转换API
- 常见的色彩空间
- RGB(多数时候使用RGB)
- HSV(图片中物体具有特征颜色,这时将图片转为HSV就能很方便找到)
- HIS(灰度饱和度)
- YCrCb(早期皮肤检测用的比较多,提取人的皮肤)
- YUV(Linux的色彩空间用的是YUV)
- 最常见的是
- HSV 与 RGB 的互相转换
- YUV 与 RGB 的互相转换
OpenCV的色彩空间API
def color_space_demo(image):
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
cv.imshow("gray", gray)
hsv = cv.cvtColor(image. cv.COLOR_BGR2HSV)
cv.imshow("hsv", hsv)
yuv = cv.cvtColor(image, cv.COLOR_BGR2YUV)
cv.imshow("yuv", yuv)
ycrcb = cv.cvtColor(image, cv.COLOR_BGR2YCrCb)
cv.imshow("ycrcb", ycrcb)
pass
inRange
- 上表中为颜色在HSV中的参数,但是取值范围各不相同
- 所以使用inRange函数直接提取对应颜色
- 表中参数 Hmax, Hmin, Vmax, Vmin, Smax, Smin 分别代表HSV色彩空间中 HSV 的取值返回所对应的颜色
def extrace_object():
cpature = cv.VideoCapture(0)
while (True):
ret, frame = cpature.read()
if not ret:
break
hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
lower_hsv = np.array([11,43,46]) # 想要提取的颜色在表格中对应的min值范围
hight_hsv = np.array([25,255,255]) # 想要提取的颜色在表格中对应的max值范围
mask = cv.inRange(hsv, lower_hsv, hight_hsv) # 我这里提取的是橙色
mask = cv.flip(mask, 1)
cv.imshow("frame", mask)
c = cv.waitKey(40)
if c ==27:
break;
pass
pass
除了匹配到的颜色是白色的,其他不匹配的都是黑色
通道分离与合并
img = cv.imread('module.png')
b, g, r = cv.split(src)
cv.imshow('blue', b)
cv.imshow('green', g)
cv.imshow('red', r)
src[ : , : ,2] = 0 # 给最后一个通道设为0,则就是 red 通道值没有
cv.imshow(src) # 效果是 只有 blue 和 green通道的混合
src = cv.merge([b,g,r]) # 相对于 split ,meige 是混合通道
像素级别的运算
- 算数运算 加,减,乘,除
- 调整亮度
- 对比度
- 合成图像
import cv2 as cv
import numpy as np
def add_demo(m1, m2):
cv.imshow("win+linux",cv.add(m1, m2))
cv.imshow("linux+win",cv.add(m2, m1))
pass
def substract_demo(m1, m2):
cv.imshow("win-linux", cv.subtract(m1, m2))
cv.imshow("linux-win", cv.subtract(m2, m1))
pass
def divide_demo(m1, m2):
cv.imshow("win / linux", cv.divide(m1, m2))
cv.imshow("linux / win", cv.divide(m2, m1))
pass
def multiply_demo(m1, m2):
cv.imshow("win * linux", cv.multiply(m1, m2))
cv.imshow("linux * win", cv.multiply(m2, m1))
pass
win = cv.imread('./samples/data/WindowsLogo.jpg')
linux = cv.imread('./samples/data/LinuxLogo.jpg')
cv.namedWindow("input window", cv.WINDOW_AUTOSIZE)
cv.imshow("windows logo", win)
cv.imshow("linux logo", linux)
add_demo(win, linux)
substract_demo(win, linux)
divide_demo(win, linux)
cv.waitKey(0)
cv.destroyAllWindows()
因为黑色是(0,0,0),所以相加并不会造成影响
百色市(255,255,255),所以不管加上多少都是知能是(255,255,255)
win+linux 和 linux+win 表达出来的图像相同
但是 win-linux 和 linux-win 减法根据传入参数有顺序变换
同理 win / linux 和 linux / win 的结果也不一样
每个方框的标题 与 计算对应
- 其他计算
def others(m1, m2):
print(cv.mean(m1)); # 分别查看BGR的参数值
print(cv.mean(m2));
M1, dev1 = cv.meanStdDev(m1)
M2, dev2 = cv.meanStdDev(m2)
print(dev1) # RBG的标准差差
print(dev2)
pass
- 逻辑运算 与,或,非
- 控制遮罩层
def logic_demo():
cv.imshow( "m1 and m2" ,cv.bitwise_and(m1, m2)) # 与
cv.imshow( "m2 and m1" ,cv.bitwise_and(m2, m1))
cv.imshow( "m1 or m2" ,cv.bitwise_or(m1, m2)) # 或
cv.imshow( "m2 or m1" ,cv.bitwise_or(m2, m1))
cv.imshow( "m1 not m2" ,cv.bitwise_not(m1, m2)) # 非
cv.imshow( "m2 not m1" ,cv.bitwise_not(m2, m1))
cv.imshow( "m1 xor m2" ,cv.bitwise_xor(m1, m2)) # 异或
cv.imshow( "m2 xor m1" ,cv.bitwise_xor(m2, m1))
- 学习了位运算,可以发现 之前我们使用HSV的时候,捕捉到的颜色是白色,未捕捉的颜色是黑色,像极了一个遮罩。
- 如果使用上述的遮罩 和 原图像 进行与运算,则可以显示捕捉到的原本的颜色
def logic_demo():
capture = cv.VideoCapture(0)
while True:
res, frame = capture.read()
if res == False:
break
hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
lower_hsv = np.array([11,43,46])
high_hsv = np.array([25,255,255])
msk = cv.inRange(hsv, lowerb=lower_hsv, upperb=high_hsv)
cv.imshow("video" ,cv.bitwise_and(frame, frame, mask=msk))
pass
c = cv.waitKey(40)
if c ==27:
break
pass
pass
实例
- 提升亮度对比度
def contrase_beightness_demo(image, c, b): #调整亮度对比度
# c 代表对比度, b 代表亮度
h, w, d = image.shape
blank = np.zeros([h, w, d], image.dtype) # 创意一个一样大小深度的图片
cv.imshow("demo", cv.addWeighted(image, c, blank, 1-c, b)) # opencv提供的api,addWeighted是线性叠加,这里就是所有像素+b,对比度大于均值增加c,小于均值减少
pass
ROI与泛洪填充
-
泛洪填充:从某个点开始,递归的填充这个区域中与其相同或相近的值,直到到达边界为止
-
上述的就是类似于Win画图中的油漆桶的效果
-
ROI区域:对于图片中该兴趣的区域,region of interest。机器视觉、图像处理中,从被处理的图像以方框、圆、椭圆、不规则多边形等方式勾勒出需要处理的区域
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
win = cv.imread('./samples/data/WindowsLogo.jpg')
cv.imshow("win", win)
cv.imshow("logo", win[60:170, 100:230]) # 代表取得的区域
截取了部分区域
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
win = cv.imread('./samples/data/WindowsLogo.jpg')
linux = cv.imread('./samples/data/LinuxLogo.jpg')
cv.imshow("win", win)
logo = cv.cvtColor(win[60:170, 100:230], cv.COLOR_BGR2GRAY) # 变成灰度
backwin = cv.cvtColor(logo, cv.COLOR_GRAY2BGR) # 重新变成三通道,但还是灰色图像,因为其他两个通道已经没有了
win[60:170, 100:230] = backwin
cv.imshow("new win", win)
cv.waitKey(0)
cv.destroyAllWindows()
让变成灰色的图像重新赋值回原图,就能修改原图的这部分区域
def fill_color_demo(image):
copyimg = image.copy()
h, w = image.shape[:2]
mask = np.zeros([h+2, w+2], np.uint8) # 一定要+2,保证周边像素可以被处理
cv.floodFill(copyimg, mask, (60, 60), (0, 255, 255), (100,100,100), (50,50,50), cv.FLOODFILL_FIXED_RANGE)
# 参数介绍,copyimg 就是传入的图像, (60,60)就是起始的判断点,(0,255,255)要改变颜色的BGR
# (100,100,100) 低值
# (50,50,50) 高值
# cv.FLOODFILL_FIXED_RANGE 表示上述范围内进行填充
cv.imshow("fill", copyimg)
pass
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
win = cv.imread('./samples/data/WindowsLogo.jpg')
linux = cv.imread('./samples/data/LinuxLogo.jpg')
cv.imshow("win", win)
fill_color_demo(win)
cv.waitKey(0)
cv.destroyAllWindows()
图像填充,填充黄色
def fill_binary():
img = np.zeros([400, 400, 3], np.uint8)
img[100:300, 100:300, :] = 255
cv.imshow("img", img)
mask = np.ones([402, 402, 1], np.uint8 )
mask[101:301, 101:301] = 0
cv.floodFill(img, mask, (200, 200), (0, 0, 255), cv.FLOODFILL_MASK_ONLY)
cv.imshow("fill img", img)
pass
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
win = cv.imread('./samples/data/WindowsLogo.jpg')
linux = cv.imread('./samples/data/LinuxLogo.jpg')
fill_binary()
cv.waitKey(0)
cv.destroyAllWindows()
另一种填充模式 mask 初始化为1,填充区域初始化为0
只有mask 为0的区域才会被填充
- folldFill(image, mask, seedPoint, newVal, rect, IoDiff, upfidd, flags)
- img(seed.x, seed.y) - IoDiff <= img(x, y) <= img(seed.x, seed.y)+upDiff
模糊操作
- 均值模糊
def blur_demo(image):
cv.imshow("blur1", cv.blur(image, (1, 15))) # 纵向
cv.imshow("blur2", cv.blur(image, (15, 1))) # 横向
# 横向或者纵向十五个像素进行一次卷积运算
pass
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
win = cv.imread('./samples/data/WindowsLogo.jpg')
blur_demo(win)
cv.waitKey(0)
cv.destroyAllWindows()
可以处理随机的噪声图片,但是效果不能确定
- 中值模糊
def media_demo(image):
cv.imshow("origin", image)
cv.imshow("blur1", cv.medianBlur(image, 5))
pass
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
win = cv.imread('./jiaoyan.jpg')
media_demo(win)
cv.waitKey(0)
cv.destroyAllWindows()
专门可以用于处理椒盐噪声的图片,图片上有黑白的斑点的图片
通过中值模糊有很好的效果
- 自定义模糊
def custom_blur_demo(image):
kernel = np.ones([5,5], np.float32)/25 # 每次对 高为5 宽为5 对像素区域做卷积,所以要处以25,使用float是为了防止溢出
dst = cv.filter2D(image, -1, kernel=kernel)
# 第一个参数 输入的图像,第二个参数 ddepth几乎可以算默认,第三个参数 自定义卷积和的算法,第三个参数输出结果
cv.imshow("my filter",dst)
pass
def blur_demo(image):
cv.imshow("blur", cv.blur(image, (5, 5)))
pass
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
win = cv.imread('./jiaoyan.jpg')
custom_blur_demo(win)
blur_demo(win)
cv.waitKey(0)
cv.destroyAllWindows()
不难发现 自定义模糊中(5,5)与 opencv定义的均值模糊(5,5)效果一样
也就是说我们可以通过自定义模糊程度达到不同的效果
例如:锐化等
我们可以不定义 二维,我们可以定义三维
kernel = np.array([[1,1,1], [1,1,1], [1,1,1]], np.float32)/9 #这里除以9 是因为array中的3*3=9
# 上述为 轻微模糊
kernel = np.array([[0,-1,0], [-1,5,-1], [0,-1,1]], np.float32) # 当然你也可以不除以9
# 上述为 锐化
算子kernel自定义要求:奇数,总和为1或0
边缘保留滤波(EPF)
- 滤波 感觉类似 卷积
- 高斯模糊仅考虑的像素空间的分布,但是如果像素差异很大,就代表这是显著特征,就不能被模糊掉
- 一般边缘地方像素差异很大,所以其名为边缘保留滤波
图的解释:
- 第一行为原图,其图像为左白右黑的有高斯噪声的图像,标点的地方是我们进行高斯模糊计算的点
- 第二行为 对选定的点进行高斯模糊计算,由于左右两边的差异很大,所以原计算的区域被去掉了已不复
- 第三行为 边缘保留滤波之后的图形
- 双边高斯模糊
def bi_demo(image):
dst = cv.bilateralFilter(image, 0, 100, 15)
cv.imshow("bi_color", dst)
pass
bilateralFilter(src, d, sigmaColor, sigmaSpace, dst=None, borderType = Node)
第一个参数:原图想
第二个参数: distance,距离,一般设置为0 减少计算量 过滤时周围每个像素领域的直径
第三个参数: 颜色差异,大一点,参数越大,临近像素将会在越远的地方mix。(越大越模糊)
第四个参数: 在coordinate space中过滤sigma。参数越大,那些颜色足够相近的的颜色的影响越大。
- 均值迁移
def bi_demo(image):
dst = cv.pyrMeanShiftFiltering(image, 10, 50)
cv.imshow("bi_color", dst)
pass
有一个类似油画的效果
有的时候会对边缘有过度的处理
图像直方图(histogram)
pip install matplotlib
- 直方图属于统计信息,卷积至于特征信息
- 直方图均衡化,图像增强算法
bin 就是频距,一个小区间,16384是总的像素个数
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
def plot_demo(image):
pass
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
win = cv.imread('./timg.jpeg')
cv.imshow("origin", win)
plot_demo(win)
cv.waitKey(0)
cv.destroyAllWindows()
def plot_demo(image):
color = ('blue', 'green', 'red')
for i, color in enumerate(color):
hist = cv.calcHist([image], [i], None, [256], [0,256])
plt.plot(hist, color=color)
plt.xlim([0, 256])
plt.show()
pass
显示红绿蓝三种颜色的直方图
openCV中需要知道如何展现直方图,以及直方图的波峰波谷所代表的含义
直方图应用
直方图均衡化
opencv 的直方图均衡化都是基于 灰度图像
彩色图像需要转换为灰度图像
- 整体直方图均衡化
- 提升图像对比度
- 提升图像清晰度
def eualHist_demo(image):
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
dst = cv.equalizeHist(gray)
cv.imshow("qualizeHist", dst)
pass
- 局部自适应直方图均衡化
- 创建局部自适应对象,传入图像就行了
- 局部最好是长宽是2的幂
def clahe_demo(image):
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
dst = clahe.apply(gray)
cv.imshow("clahe", dst)
pass
从图片中的对比可以看出来,局部的均衡化比整体的均衡化更适宜
整体均衡化有的时候会出现过度的情况,这种时候就需要局部均衡化