(有的运行结果没弄上去,但文中代码本人亲测均通过;至于有人因版本问题出现个别错误,我相信对于大家应该没什么问题,文档就是很好的辅助学习资料)
OpenCV是 Open Source Computer Vision Library OpenGL是 Open Graphics Library OpenCV主要是提供图像处理和视频处理的基础算法库,还涉及一些机器学习的算法。比如你想实现视频的降噪、运动物体的跟踪、目标(比如人脸)的识别这些都是CV的领域 OpenGL则专注在Graphics,3D绘图。 其实两者的区别就是Computer Vision和Computer Graphics这两个学科之间的区别,前者专注于从采集到的视觉图像中获取信息,是用机器来理解图像;后者是用机器绘制合适的视觉图像给人看。
一般opencv有四个模块: opencv-python: 只包含opencv库的主要模块. 一般不推荐安装. opencv-contrib-python: 包含主要模块和contrib模块, 功能基本完整, 推荐安装. opencv-python-headless: 和opencv-python一样, 但是没有GUI功能, 无外设系统可用. opencv-contrib-python-headless: 和opencv-contrib-python一样但是没有GUI功能. 无外设系统可用. 因此一般来说都会选择安装opencv-contrib-python window环境下安装命令:pip install opencv-contrib-python -i https://pypi.tuna.tsinghua.edu.cn/simple (当然pip命令有可能也需要安装更新之类的,网上查找更新命令即可,如果可以运行当我没说;如果直接使用安装命令pip install opencv-contrib-python会比较慢,所以我使用了国内资源) 不要同时安装opencv-python和opencv-contrib-python 1.可以在window终端下进行简单的测试(当然我写的时候按python代码块处理了,所以出现下面情况)
>>>import cv2 >>>cv2.__version__ '4.2.0'2.在pycharm中的测试(pycharm的安装和破解网上很多,我这里不进行解释;还有在pycharm中进行测试的时候,虽然cv2模块已经下载好,但是pycharm中并没有导入,可参考下面网址,将下载好的模块导入到pycharm中【https://blog.csdn.net/ShadowFox_/article/details/82871575】) 如果可以输出图片,并且在关闭后输出Hi,Python!那么表示安装成功(第三行代码处图片路径最好是英文;代码中注意W是大写,如果不是同样会输出图片,但关闭图片之后不会输出Hi,Python!)
下面的五个横框表示基础模块。右边是扩展的模块,也就是opencv-contrib-python中的contrib,下载该模块之后才可以使用。上面的11个模块是基于下面五个基础模块的。
特别注意第10行代码处,参数cv.CAP_DSHOW一开始我没有写,在关闭摄像头时出现下列报错,所以我加上该参数,这是由于采用默认的API控制出错 [ WARN:0] global C:\projects\opencv-python\opencv\modules\videoio\src\cap_msmf.cpp (674) SourceReaderCB::~SourceReaderCB terminating async callback
区别: zeros:生成全0的矩阵 ones:生成全1的矩阵
对于单通道,像素在0-255之间变化,图像在黑与白之间变化;而对于多通道,三种通道bgr的出现,使图像出现彩色。 对于zeros和ones来说都可以处理多通道和单通道,唯一区别就是一个全0,一个全1,所以zeros还是需要用到ones函数进行像素的设置,全0进行乘法*的运算永远是全0,这样是达不到效果的
这个模型中颜色的参数分别是:色调Hue(H),饱和度Saturation(S),明度Value(V)。
import cv2 as cv import numpy as np def extract_object_demo(): """ inRange函数色彩捕捉 """ video = cv.VideoCapture("D:/image/white.mp4") while True: ret, frame = video.read() if not ret: break # 通过inrange函数进行色彩捕捉,对白色进行捕捉,这个看自己视频中要捕捉颜色,然后根据下方表确定范围 hsv = cv.cvtColor(frame, cv.COLOR_RGB2HSV) lower_hsv = np.array([0, 0, 221]) upper_hsv = np.array([180, 30, 255]) mask = cv.inRange(hsv, lowerb=lower_hsv, upperb=upper_hsv) cv.imshow("mask", mask) # 逻辑与运算,后面几节出现,对与运算加上mask也可以出现图像,mask是可选参数,具体参数含义可查看opencv官方文档 dst = cv.bitwise_and(frame, frame, mask=mask) cv.imshow("dst", dst) cv.imshow("video", frame) c = cv.waitKey(40) if c == 27: break def color_space_demo(image): # 色彩空间转换API,当然还有很多,rgb,gray,hsv,yuv,ycrcb比较常见,尤其rgb到hsv和yuv的转换记牢 gray = cv.cvtColor(image, cv.COLOR_RGB2GRAY) cv.imshow("gray", gray) hsv = cv.cvtColor(image, cv.COLOR_RGB2HSV) cv.imshow("hsv", hsv) yuv = cv.cvtColor(image, cv.COLOR_RGB2YUV) cv.imshow("yuv", yuv) ycrcb = cv.cvtColor(image, cv.COLOR_RGB2YCrCb) cv.imshow("ycrcb", ycrcb) src = cv.imread("D:/image/dingxia.jpg") # cv.namedWindow("start", cv.WINDOW_AUTOSIZE) # cv.imshow("start", src) extract_object_demo() # color_space_demo(src) cv.waitKey(0) cv.destroyAllWindows()opencv自带图片色素的处理函数:
相加:add()
相减:subtract()
相乘:multiply()
相除:divide()
原理就是:通过获取两张(一次只能是两张)个图片的同一个位置的色素值来实现运算。
运算的要求:两张图片的shape要一样。
例图:(自己保存为图片格式即可)
import cv2 as cv import numpy as np def add_pixels(m1, m2): dst = cv.add(m1, m2) cv.imshow("add", dst) def subtract_pixels(m1, m2): dst = cv.subtract(m1, m2) cv.imshow("subtract", dst) # cv.imwrite("D:/image/linux_window.jpg", dst) linux-window还是比较好看就保存了 def multiply_pixels(m1, m2): dst = cv.multiply(m1, m2) cv.imshow("multiply", dst) def divide_pixels(m1, m2): dst = cv.divide(m1, m2) cv.imshow("divide", dst) # 均值和方差 mean表示均值,meanStdDev可返回均值和方差 def others(m1, m2): M1, dev1 = cv.meanStdDev(m1) M2, dev2 = cv.meanStdDev(m2) # 取和图像m1相同的高宽,【:2】涉及切片,左实右虚,取shape前两个 h, w = m1.shape[:2] print(M1) print(M2) print(dev1) print(dev2) # 对于像素相同的矩阵,方差均为0,这里使用了全0矩阵,所以输出的均值和方差均为0 # 若使用ones全1矩阵,那么输出均值为1,方差为0 img = np.zeros([h, w], np.uint8) m, dev = cv.meanStdDev(img) print(m) print(dev) src1 = cv.imread("D:/image/linux.jpg") src2 = cv.imread("D:/image/window.jpg") # cv.namedWindow("Hello opencv!", cv.WINDOW_AUTOSIZE)有没有影响不大,除非对图片有大小变化需求 cv.imshow("Hello linux!", src1) cv.imshow("Hello window!", src2) # add add_pixels(src1, src2) # subtract subtract_pixels(src1, src2) # multiply multiply_pixels(src1, src2) # divide divide_pixels(src1, src2) others(src1, src2) cv.waitKey(0) cv.destroyAllWindows() print("Hi,Python!")opencv自带图片色素的处理函数:
与:bitwise_add() 全1则1,其他为0
或:bitwise_or() 有1为1,其他为0
非:bitwise_not() 1为0,0为1
异或:bitwise_xor() 相同为0,不同为1
对于0 1 的来说,这四种操作比较好理解,对于像素值来说要采用下表方式进行按位与或非、异或操作:
import cv2 as cv import numpy as np def logic_demo(m1, m2): """ 逻辑运算 与或非异或 :param m1: 第一张图片 :param m2: 第二张图片 """ dst = cv.bitwise_and(m1, m2) cv.imshow("and", dst) dst = cv.bitwise_or(m1, m2) cv.imshow("or",dst) dst = cv.bitwise_not(m1) cv.imshow("not1", dst) dst = cv.bitwise_not(m2) cv.imshow("not2", dst) dst = cv.bitwise_xor(m1, m2) cv.imshow("xor", dst) def contrast_brightness_demo(image, c, b): """ 调整图像对比度和亮度 :param image: 要调整的图片 :param c: 对比度 :param b: 亮度 """ h, w, ch = image.shape # blank是一个全0的矩阵,是黑色的 blank = np.zeros([h, w, ch], image.dtype) dst = cv.addWeighted(image, c, blank, 1-c, b) cv.imshow("demo", dst) src1 = cv.imread("D:/image/linux.jpg") src2 = cv.imread("D:/image/window.jpg") # cv.namedWindow("Hello opencv!", cv.WINDOW_AUTOSIZE) cv.imshow("Hello linux!", src1) cv.imshow("Hello window!", src2) # 逻辑运算 与或非异或 # logic_demo(src1, src2) # 调整图像对比度和亮度 contrast_brightness_demo(src2, 1.5, 0) cv.waitKey(0) cv.destroyAllWindows() print("Hi,Python!")addWeighted(InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray dst, int dtype=-1);
一共有七个参数:前4个是两张要合成的图片及它们所占比例,第5个double gamma起微调作用,第6个OutputArray dst是合成后的图片,第七个输出的图片的类型(可选参数,默认-1)
有公式得出两个图片加成输出的图片为:dst=src1alpha+src2beta+gamma
从被处理的图像以方框、圆、椭圆、不规则多边形等方式勾勒出需要处理的区域,称为感兴趣区域ROI(region of interest)
import cv2 as cv import numpy as np src = cv.imread("D:/image/dingxia.jpg") cv.namedWindow("Hello opencv!", cv.WINDOW_AUTOSIZE) cv.imshow("Hello opencv!", src) # 切割图像,范围的查找要自己慢慢试 img = src[50:200, 230:350] # 将切割出的图像填充变成灰色 gray = cv.cvtColor(img, cv.COLOR_RGB2GRAY) # 下面这行代码是为了增加通道,因为gray为灰度图像,是单通道,而src是三通道,所以需要cvtColor一下,将其转换为三通道 # 而且由于灰度图像无法再恢复成彩色图像,所以cvtColor之后,只会增加通道数,图像表现还是灰色 backface = cv.cvtColor(gray, cv.COLOR_RGB2BGR) # 合并图像 src[50:200, 230:350] = backface cv.imshow("gray", src) cv.waitKey(0) cv.destroyAllWindows() print("Hi,Python!")floodFill函数:漫水填充算法
基本思想: 所谓漫水填充,简单来说,就是自动选中了和种子点相连的区域,接着将该区域替换成指定的颜色,这是个非常有用的功能,经常用来标记或者分离图像的一部分进行处理或分析.漫水填充也可以用来从输入图像获取掩码区域,掩码会加速处理过程,或者只处理掩码指定的像素点.
以此填充算法为基础,类似photoshop的魔术棒选择工具就很容易实现了。漫水填充(FloodFill)是查找和种子点联通的颜色相同的点,魔术棒选择工具则是查找和种子点联通的颜色相近的点,将和初始种子像素颜色相近的点压进栈作为新种子
在OpenCV中,漫水填充是填充算法中最通用的方法。且在OpenCV 2.X中,使用C++重写过的FloodFill函数有两个版本。一个不带掩膜mask的版本,和一个带mask的版本。这个掩膜mask,就是用于进一步控制哪些区域将被填充颜色(比如说当对同一图像进行多次填充时)。这两个版本的FloodFill,都必须在图像中选择一个种子点,然后把临近区域所有相似点填充上同样的颜色,不同的是,不一定将所有的邻近像素点都染上同一颜色,漫水填充操作的结果总是某个连续的区域。当邻近像素点位于给定的范围(从loDiff到upDiff)内或在原始seedPoint像素值范围内时,FloodFill函数就会为这个点涂上颜色。
官方定义为: 第一个版本的floodfill(该版本没有mask参数)
int floodFill(InputOutputArray image, Point seedPoint, Scalar newVal, Rect* rect=0, Scalar loDiff=Scalar(), Scalar upDiff=Scalar(), int flags=4 )第二个版本的floodfill
int floodFill(InputOutputArray image, InputOutputArray mask, Point seedPoint,Scalar newVal, Rect* rect=0, Scalar loDiff=Scalar(), Scalar upDiff=Scalar(), int flags=4 )以第二个版本进行介绍,但不知道为什么,python中调用这个函数,Rect* rect=0这个参数没有,剩下7个参数 ,至于Rect*类型的rect,有默认值0,一个可选的参数,用于设置floodFill函数将要重绘区域的最小边界矩形区域。在pycharm中没有暂时不考虑
通俗解释:floodFill( 1.操作的图像, 2.掩模, 3.起始像素点,4.填充的颜色, 6.填充颜色的低值, 7.填充颜色的高值 ,8.填充的方法)第五个rect忽略
参数6.填充颜色的低值就是:参数3位置的像素 减去 参数6
参数7.填充颜色的高值就是:参数3位置的像素 加上 参数7
即是这两个数值之间的色素替换为参数4的颜色
彩色图像一般是FLOODFILL_FIXED_RANGE 指定颜色填充
还有一种是FLOODFILL_MASK_ONLY,mask的指定的位置为零时才填充,不为零不填充
最后一个参数的比较: FLOODFILL_FIXED_RANGE - 如果设置为这个标识符的话,就会考虑当前像素与种子像素之间的差,否则就考虑当前像素与其相邻像素的差。也就是说,这个范围是浮动的。 FLOODFILL_MASK_ONLY - 如果设置为这个标识符的话,函数不会去填充改变原始图像 (也就是忽略第四个参数newVal),而是去填充掩模图像(mask)。这个标识符只对第二个版本的floodFill有用,因第一个版本里面压根就没有mask参数。
至于指定颜色填充的过程,就和FLOODFILL_FIXED_RANGE解释的一样,会和相邻的像素作比较,采用泛洪的方法填充(个人理解,没有找到可信度高的介绍,很遗憾)
该链接是我参考的一篇博客,专门讲解floodfill函数,有兴趣可以看一看 【https://blog.csdn.net/qq_27901569/article/details/49934763】
import cv2 as cv import numpy as np # 指定颜色填充 def fill_color_demo(image): copyImg = image.copy() h, w = image.shape[:2] # zeros和ones不写通道数默认单通道 # mask它应该为单通道、8位、长和宽上都比输入图像 image 大两个像素点的图像 +2是官方函数要求 # 需要注意的是,漫水填充不会填充掩膜mask的非零像素区域。也就是只填充掩膜mask的0的区域 mask = np.zeros([h+2, w+2], np.uint8) cv.floodFill(copyImg, mask, (100, 100), (0, 255, 255), (100, 100, 100), (50, 50, 50), cv.FLOODFILL_FIXED_RANGE) cv.imshow("fill_color_demo", copyImg) # 指定位置填充 def fill_binary(): image = np.ones([400, 400, 3], np.uint8) image[100:300, 100:300, :] = 255 cv.imshow("fill_binary", image) # zeros和ones不写通道数默认单通道 mask = np.ones([402, 402], np.uint8) # 将要填充位置的像素设为0 mask[101:301, 101:301] = 0 cv.floodFill(image, mask, (100, 100), (0, 0, 255), cv.FLOODFILL_MASK_ONLY) cv.imshow("filled binary", image) src = cv.imread("D:/image/dingxia.jpg") cv.namedWindow("Hello opencv!", cv.WINDOW_AUTOSIZE) cv.imshow("Hello opencv!", src) # 第一个使用图片填充,第二个自己在函数内随便建了一个图像进行填充的 fill_color_demo(src) fill_binary() cv.waitKey(0) cv.destroyAllWindows() print("Hi,Python!")1.均值模糊函数blur():定义:blur(src,ksize,dst=None, anchor=None, borderType=None)
定义是有5个参数,但最后三个均为none,所以也就2个参数
src:要处理的原图像
ksize: 周围关联的像素的范围:代码中(5,5)就是5*5的大小,就是计算这些范围内的均值来确定中心位置的大小
2.中值模糊函数medianBlur(): 定义:medianBlur(src, ksize, dst=None)
ksize与blur()函数不同,不是矩阵,而是一个数字,例如为5,就表示了5*5的方阵
3.高斯平滑函数GaussianBlur():定义:GaussianBlur(src, ksize, sigmaX, dst=None, sigmaY=None, borderType=None)
sigmaX:标准差
4.双边滤波函数bilateralFilter():定义:bilateralFilter(src, d, sigmaColor, sigmaSpace, dst=None, borderType=None) d:邻域直径 sigmaColor:颜色标准差 sigmaSpace:空间标准差
5.自定义模糊 使用的函数为:filter2D():定义为filter2D(src,ddepth,kernel)
ddepth:深度,输入值为-1时,目标图像和原图像深度保持一致
kernel: 卷积核(或者是相关核),一个单通道浮点型矩阵
修改kernel矩阵即可实现不同的模糊
原图: lena的加入椒盐噪声的彩色图片没找到,只有找到灰色图像,可以用这个加入椒盐噪声的lena图片对均值和中值进行比较,中值会取得比较好的效果
import cv2 as cv import numpy as np # 均值模糊 def blur_demo(image): # (5,5)分别表示横向和竖向,(5,5)比较常用 dst = cv.blur(image, (5, 5)) cv.imshow("blue_demo", dst) # 中值模糊,用于处理椒盐噪声,比均值处理效果好 def median_blur_demo(image): dst = cv.medianBlur(image, 5) cv.imshow("median_blur_demo", dst) # 高斯模糊 def gaussian_blur_demo(image): dst = cv.GaussianBlur(image, (5, 5), 2) cv.imshow("gaussian_blur_demo", dst) # 双边滤波 def bilateralFilter_demo(image): dst = cv.bilateralFilter(image, 5, 5, 2) cv.imshow("bilateralFilter_demo", dst) # 自定义模糊、锐化处理; # 总和为0,做边缘梯度;奇数或者总和为1,做增强,锐化处理 def custom_blur_demo(image): # 自定义矩阵,并防止数值溢出(第一种表示方法) # kernel = np.ones([5, 5], np.float32)/25 # (第二种表示方法) # kernel = np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]], np.float32)/9 # 下方数据为标准的锐化算子(一般数据要求奇数或总和为1) kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]], np.float32) dst = cv.filter2D(image, -1, kernel=kernel) cv.imshow("median_blur_demo", dst) src = cv.imread("D:/image/lena.jpg") cv.namedWindow("Hello opencv!", cv.WINDOW_AUTOSIZE) cv.imshow("Hello opencv!", src) # 均值模糊 # blur_demo(src) # 中值模糊 # median_blur_demo(src) # 高斯模糊 # gaussian_blur_demo(src) # 双边滤波 # bilateralFilter_demo(src) # 自定义模糊 custom_blur_demo(src) cv.waitKey(0) cv.destroyAllWindows() print("Hi,Python!")结果说明高斯模糊对高斯噪声有抑制作用
这个堪比磨皮,非常好玩!
高斯双边 dst = cv.bilateralFilter( src,d,sigmaColor,sigmaSpace [,dst [,borderType]] ) dst也就是输出,和borderType都是可选参数 src 源8位或浮点,1通道或3通道图像。 dst 与src大小和类型相同的目标映像。 d 滤波期间使用的每个像素邻域的直径。如果它不是正值,则从sigmaSpace计算得出。 sigmaColor 在色彩空间中过滤sigma。参数的较大值表示像素邻域内的其他颜色(请参见sigmaSpace)将混合在一起,从而导致较大区域的半均等颜色。 sigmaSpace 在坐标空间中过滤sigma。该参数的值越大,表示越远的像素就会相互影响,只要它们的颜色足够接近即可(请参见sigmaColor)。当d> 0时,它指定邻域大小,而不考虑sigmaSpace。否则,d与sigmaSpace成比例。 borderType 用于推断图像外部像素的边框模式
均值迁移 dst = cv.pyrMeanShiftFiltering( src,sp,sr [,dst [,maxLevel [,termcrit]]] ) dst也就是输出,和maxLevel、termcrit都是可选参数 src 源8位3通道图像。 dst 与源格式和大小相同的目标图像。 SP 空间窗口半径。 SR 彩色窗口半径。 maxLevel 用于分割的金字塔的最高级别。 termcrit 终止标准:何时停止均值漂移迭代。
import cv2 as cv import numpy as np # 高斯双边 def bi_demo(image): dst = cv.bilateralFilter(image, 0, 100, 15) cv.imshow("bi_demo", dst) # 均值迁移 def shift_demo(image): dst = cv.pyrMeanShiftFiltering(image, 10, 50) cv.imshow("shift_demo", dst) src = cv.imread("D:/image/dingxia.jpg") cv.namedWindow("Hello opencv!", cv.WINDOW_AUTOSIZE) cv.imshow("Hello opencv!", src) bi_demo(src) shift_demo(src) cv.waitKey(0) cv.destroyAllWindows() print("Hi,Python!")运行结果如下: 原图像 高斯双边后图像: 均值迁移后图像:
