《OpenCV轻松入门:面向Python》学习笔记

《OpenCV轻松入门:面向Python》阅读笔记


基本信息

  • 作者:李立宗
  • 阅读日期:从 2025年2月16日 开始

第一章 OpenCV入门

  • OpenCV是一个开源的计算机视觉库,学习本书内容掌握函数使用,同时理解黑盒其中的算法原理,以便更好地使用。
  • 可通过pip、conda进行安装,库名称opencv-python,也可以在官网下载离线包。链接:https://pypi.org/project/opencv-python/
# pip安装方式
pip install opencv-python
# or
pip install D:\anaconda\Lib\opencv_python-3.4.3.18-cp37-cp37m-win_amd64.whl # 官网下载的文件路径

# conda安装方式
conda create -n opencv python=3.7
conda activate opencv
conda install opencv
  • 图像基本操作
import cv2 # 导入OpenCV库

img = cv2.imread(filename, flags) # 读取图像,flags通常为 cv2.IMREAD_UNCHANGED

cv2.imshow(windowName, img) # 显示图像

key = cv2.waitKey([delay]) # 等待输入
# delay 等待键盘触发时间,0或-1表示永久等待

cv2.destoryAllWindow() # 销毁所有窗口

cv2.imwrite(filename, img, params) # 保存图像

cv2.imread flags参数如下:

  • OpenCV贡献库,由社区开发维护,其包含视觉应用比OpenCV更丰富,库名称opencv_contrib
    OpenCV贡献库中包含了非常多的扩展模块,举例如下。
    • bioinspired:生物视觉模块。
    • datasets:数据集读取模块。
    • dnn:深度神经网络模块。
    • face:人脸识别模块。
    • matlab:MATLAB接口模块。
    • stereo:双目立体匹配模块。
    • text:视觉文本匹配模块。
    • tracking:基于视觉的目标跟踪模块。
    • ximgpro:图像处理扩展模块。
    • xobjdetect:增强2D目标检测模块。
    • xphoto:计算摄影扩展模块。

通过语句可直接安装

pip install opencv-contrib-python

第二章 图像处理基础

  • 想学会Opencv必须熟练掌握Numpy库,尤其是Numpy.array的运用

图像类型

  • 二值图像:仅包含黑色和白色的图像。
  • 灰度图像:采用了更多数值来体现更多颜色,灰度处理分为256个灰度级,用数值区别[0,255]来表示,正好可以用一个字节来表示。
  • 彩色图像:在RGB色彩空间中存在R(Red红色)G(Green绿色)B(Blue蓝色)三个通道,每个色彩通道数值都在[0,255]之间,用三个通道组合来表示颜色。
  • 使用numpy生成数值都是0的二维数组,以此表示二维图像。
    import cv2
    import numpy as np
    img=np.zeros((8,8), dtype=np.uint8)
    print("img=\n", img)
    cv2.imshow("one", img)
    print("读取像素点img[0,3]=", img[0,3])
    img[0,3]=255
    print("修改后img=\n", img)
    print("读取修改后像素点img[0,3]=", img[0,3])
    cv2.imshow("two", img)
    cv2.waitKey()
    cv2.destroyAllWindows()

代码分析如下:
使用numpy生成了8x8的大小图像,其所有值都是0,数值类型是numpy.uint8。该数组可以看成是一个黑色的图像。
通过访问img[0,3]修改数组,从而修改图像。

  • 读取并修改灰度图像
    import cv2
    img=cv2.imread("lena.bmp",0)
    cv2.imshow("before", img)
    for i in range(10,100):
        for j in range(80,100):
            img[i, j]=255
    cv2.imshow("after", img)
    cv2.waitKey()
    cv2.destroyAllWindows()

代码分析如下:
通过循环修改像素,将第10行到99行与第80列到100列的交叉区域修改为255(白色)

  • Opencv内以三维数组的形式表示BGR(RGB)
    彩色图像生成代码示例如下
    import numpy as np
    import cv2
    #-----------蓝色通道值--------------
    blue=np.zeros((300,300,3), dtype=np.uint8)
    blue[:, :,0]=255
    print("blue=\n", blue)
    cv2.imshow("blue", blue)
    #-----------绿色通道值--------------
    green=np.zeros((300,300,3), dtype=np.uint8)
    green[:, :,1]=255
    print("green=\n", green)
    cv2.imshow("green", green)
    #-----------红色通道值--------------
    red=np.zeros((300,300,3), dtype=np.uint8)
    red[:, :,2]=255
    print("red=\n", red)
    cv2.imshow("red", red)
    #-----------释放窗口--------------
    cv2.waitKey()
    cv2.destroyAllWindows()
  • numpy库中的item(x,y,bgr)可以高效访问像素点,itemset(x,y,bgr)可以用来修改像素值

  • 使用numpy的随机函数来生成一个随机的灰度图像

    import numpy as np
    import cv2
    img=np.random.randint(0,256, size=[256,256], dtype=np.uint8)
    cv2.imshow("demo", img)
    cv2.waitKey()   
    cv2.destroyAllWindows()
  • 使用numpy随机生成彩色图像。
    import cv2
    import numpy as np
    img=np.random.randint(0,256, size=[256,256,3], dtype=np.uint8)
    cv2.imshow("demo", img)
    cv2.waitKey()
    cv2.destroyAllWindows()

感兴趣区域(ROI)

  • ROI全称Region of interest,在设定了感兴趣区域ROI之后,就可以对整个区域进行操作。例如将A区域的ROI复制的变量B后,可以将B赋值给C区域。
      a=img[200:400,200:400]
      img[200:400,600:800]=a

对ROI脸部进行打码,代码如下:

      import cv2
      import numpy as np
      a=cv2.imread("lenacolor.png", cv2.IMREAD_UNCHANGED)
      cv2.imshow("original", a)
      face=np.random.randint(0,256, (180,100,3))
      a[220:400,250:350]=face
      cv2.imshow("result", a)
      cv2.waitKey()
      cv2.destroyAllWindows()

通道操作

在图像处理的过程中,可以根据需要将BGR三个通道拆分和合并

  • 使用索引拆分:
  b = img[:,:,0]
  g = img[:,:,1]
  r = img[:,:,2]
  • 通过OpenCV函数拆分
  b,g,r = cv2.split(img)
  • 通道合并操作
  img = cv2.merge([b,g,r])
  • 获取图像属性
    在图像处理的过程中需要获取图像的属性,常用的图像属性如下:

    • shape:如果是彩色图像,返回包含行、列、通道数的数组;如果是灰度图像或二值图像,仅返回行、列。
    • size:返回图像的像素数目。其值为返回"行通道数",如果是灰度或二值图像,通道数为1。
    • dtype:返回图像的数据类型。

    读取属性代码示例:

          import cv2
          gray=cv2.imread("lena.bmp",0)
          color=cv2.imread("lenacolor.png")
          print("图像gray属性:")
          print("gray.shape=", gray.shape)
          print("gray.size=", gray.size)
          print("gray.dtype=", gray.dtype)
          print("图像color属性:")
          print("color.shape=", color.shape)
          print("color.size=", color.size)
          print("color.dtype=", color.dtype)
    

    输出结果:

          图像gray属性:
          gray.shape= (256, 256)
          gray.size= 65536
          gray.dtype= uint8
          图像color属性:
          color.shape= (512, 512, 3)
          color.size= 786432
          color.dtype= uint8
    

第三章 图像运算

图像加法运算

在图像a(像素值a)与图像b(像素值b)进行加法运算时,遵循以下原则:a+b = mod(a+b,256)

  • add函数
    在使用add函数相加时,数值超出255时与直接运算符相加不同,取值为255
    用如下代码示例,来对比结果不同:
    import cv2
    a=cv2.imread("lena.bmp",0)
    b=a
    result1=a+b
    result2=cv2.add(a, b)
    cv2.imshow("original", a)
    cv2.imshow("result1", result1)
    cv2.imshow("result2", result2)
    cv2.waitKey()
    cv2.destroyAllWindows()

img

  • 从上述运算结果可以看出:
    • 使用加号运算符计算图像像素值的和时,将和大于255的值进行了取模处理,取模后大于255的这部分值变得更小了,导致本来应该更亮的像素点变得更暗了,相加所得的图像看起来并不自然。
    • 使用函数cv2.add()计算图像像素值的和时,将和大于255的值处理为饱和值255。图像像素值相加后让图像的像素值增大了,图像整体变亮。

图像加权和

所谓图像加权和,就是在图像相加时,将每幅图像的权重考虑进来

dst=saturate(src1×α+src2×β+γ)

代码如下:

    dst=cv2.addWeighted(src1, alpha, src2, beta, gamma)
    # 可以上式理解为(图像1,系数1,图像2,系数2,亮度调节量),其中gamma亮度调节量为必选参数,可以为0

为了直观的感受到加权和的作用,我们来实际操作一下看看:

    import cv2
    a=cv2.imread("boat.bmp")
    b=cv2.imread("lena.bmp")
    result=cv2.addWeighted(a,0.6, b,0.4,0)
    cv2.imshow("boat", a)
    cv2.imshow("lena", b)
    cv2.imshow("result", result)
    cv2.waitKey()
    cv2.destroyAllWindows()

程序运行结果如下:
img

按位逻辑运算

  • 与运算(重点)
    根据与运算的特特点,可以构造一幅掩模图像,按位保留图像中被掩模的指定部分
    import cv2
    import numpy  as np
    a=cv2.imread("lena.bmp",0)
    b=np.zeros(a.shape, dtype=np.uint8)
    b[100:400,200:400]=255
    b[100:500,100:200]=255
    c=cv2.bitwise_and(a, b)
    cv2.imshow("a", a)
    cv2.imshow("b", b)
    cv2.imshow("c", c)
    cv2.waitKey()
    cv2.destroyAllWindows()

img
可以看到被掩模指定的部分都留在图像中。

  • 除此之外还有或、非、异或运算,这里有由于书中没有用途示例,只要懂这几个概念都知道作用,作者一笔带过。

位平面分解

将灰度图像中处于同一比特位上的二进制像素进行组合,得到一幅二进制画像,该图被称为灰度图像的一个位平面。
在8位灰度图中,每一个像素使用8位二进制值来表示,其值的范围在[0,255]之间。可以将其中的值表示为:
img

提取灰度位平面

# 提取灰度位平面
img = cv2.imread("dog.jpg", cv2.IMREAD_GRAYSCALE)
cv2.imshow("origin",img)
r, c = img.shape
bit_matrix = np.zeros((r,c,8), dtype=np.uint8)
for i in range(8):  #各个位平面提取矩阵
    bit_matrix[:,:,i] = 2**i
result = np.zeros((r,c,8), dtype=np.uint8)
for i in range(8):
    result[:,:,i] = cv2.bitwise_and(img, bit_matrix[:,:,i])
    mask = result[:,:,i] > 0    #返回位平面结果数组
    result[mask] = 255
    cv2.imshow(str(i), result[:,:,i])

cv2.waitKey()
cv2.destroyAllWindows()

程序结果如下:
img

  • 通过提取灰度图像像素点二进制像素值的每一比特位的组合,可以得到多个位平面图像。图像中全部像素值的ai值所构成的位平面,称为第i个位平面(第i层)​。在8位灰度图中,可以组成8个二进制值图像,即可以将原图分解为8个位平面。
  • 根据上述分析,像素值中各个ai的权重是不一样的:
    • a7的权重最高,所构成的位平面与原图像相关性最高,该位平面看起来通常与原图像最类似。
    • a0权重最低,所构成的位平面与原图像相关性最低,该平面看起来通常是杂乱无章的。

提取彩色位平面

# 提取彩色位平面
img = cv2.imread("dog.jpg", cv2.IMREAD_UNCHANGED)
cv2.imshow("origin", img)
r,c, channels = img.shape
x = np.zeros((r,c,8), dtype=np.uint8)
for i in range(8):
    x[:,:,i] = 2**i
result = np.zeros((r,c,channels, 8), dtype=np.uint8)
for channel in range(channels):
    for i in range(8):
        result[:,:,channel, i] = cv2.bitwise_and(img[:,:,channel], x[:,:,i])
        mask = result[:,:,channel,i] > 0
        result[mask] = 255
        color = "blue" if channel ==0 else "green" if channel==1 else "red"
        cv2.imshow(f"{color} {i}bit",result[:,:,channel,i])
        cv2.moveWindow(f"{color} {i}bit", i*300, channel*400)

图像加解密

通过对原始图像与密钥图像进行按位异或,可以实现加密。将加密的图像与密钥图像按位异或,就可以获得原始图像
假设原始图像a与密钥图像b进行异或运算

xor(a,b)=c

则可以得到

xor(c,b)=a

xor(c,a)=b

加解密图像代码如下:
    # 图像加密解密
    img = cv2.imread("dog.jpg", cv2.IMREAD_UNCHANGED)
    cv2.imshow("origin", img)
    h,w,c = img.shape
    key_img = cv2.imread("cat1.jpg", cv2.IMREAD_UNCHANGED)
    key_img = key_img[:h,:w]
    cv2.imshow("key image", key_img)
    encode_img = cv2.bitwise_xor(img, key_img)
    cv2.imshow("encode img", encode_img)
    decode_img = cv2.bitwise_xor(encode_img, key_img)
    cv2.imshow("decode img", decode_img)
    cv2.waitKey()
    cv2.destroyAllWindows()

img

数字水印

  • 最低有效位(Least Significant Bit, LSB)指的是一个二进制中的第0位(即最低位)。最低有效位隐藏信息是指将一个需要隐藏的二值图像信息嵌入载体图像的有效最低位,即将载体图像的最低有效位层替换为当前需要隐藏的二值图像,从而实现将二值图像隐藏的目的。
  • 原理
    • 嵌入过程:将载体图像的第0个位平面替换为数字水印信息(一幅二值图像)
    • 提取过程:将载体图像的第0个位平面提取出来,得到数字水印信息。
posted @ 2025-02-23 12:42  sh0ut  阅读(114)  评论(0)    收藏  举报