OpenCV图像处理基础

191 阅读6分钟

1、计算机中的图像

image.png

  • 计算机中的图片数据结构如上图所示,实际上是通过多维矩阵对图像数据进行存储的。
  • 一般彩色图片为三维[w,h,c],分别是宽、高、通道数(channel),如果是彩色图片对应就是R(Red)、G(Green)、B(Blue)三通道,即c值为3,如果是灰度图,可以直接用二维矩阵 [w, h] 表示。
  • 矩阵中的每个元素代表一个像素的值,其范围通常为 0~255,其中 0 表示黑色(无亮度),255 表示白色(最大亮度)。在某些深度学习框架中,图片数据可能会被归一化到 [0, 1] 的浮点数范围。
  • 除了 RGB 颜色空间,还有其他常用的颜色空间,例如 HSV 和 YUV,它们在特定应用场景下可能更有优势。HSV在偏色检查场景下更有优势,而YUV更多应用于视频编码场景

1.2 图片读取

import cv2

# 读取图片
image = cv2.imread("test.png")  # 得到是一个numpy数组
# 获取数组的结构
print(image.shape)
# 将图片转换成灰度图
image = cv2.imread("test.png", cv2.IMREAD_GRAYSCALE)
# 再次获取数组的结构
print(image.shape)

因为cv2.imread不支持非 ASCII 字符的路径。因此如果路径中带中文字符,需要对上述方法进行优化

import re

import cv2
import numpy as np
from PIL import Image


def contains_non_ascii_chars(path_: str) -> bool:
    """
    检查图片路径是否含有非ASCII字符
    :param path_: 图片路径
    """
    try:
        # method 1. 正则匹配
        pattern = re.compile(r'[^\x00-\x7F]')
        return bool(pattern.search(path_))
        # # method 2.ord方法,ord方法返回字母的Unicode 编码值,ASCII的编码值范围为0~127
        # for char in path_:
        #     if ord(char) > 127:
        #         return True
        # return False
    except Exception as e:
        print(f"check fail: {e.__str__()}")


def read_image(image_path: str) -> None:
    """
    读取图片
    :param image_path:图片路径
    """
    try:
        if contains_non_ascii_chars(image_path):
            # method 1. 通过imdecode读取出来的rgb,需要通过参数cv2.IMREAD_COLOR转换成bgr,以支持opencv处理
            image_obj = cv2.imdecode(np.fromfile(image_path, dtype=np.uint8), cv2.IMREAD_COLOR)

            # # method 2. 使用 Unicode 路径读取图片(这个方法挑环境版本,兼容性很差)
            # image_obj = cv2.imread(image_path.encode('utf-8').decode('utf-8'))

            # # method 3. 使用 Pillow 库读取图片
            # # 使用 Pillow 读取图片
            # pil_image = Image.open(image_path)
            # # 将 Pillow 图像转换为 OpenCV 格式(BGR)
            # image_obj = cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR)
        else:
            image_obj = cv2.imread(image_path)

        print(image_obj.shape)
    except Exception as e:
        print(f"read image fail: {e.__str__()}")

read_image(r'D:\XXX\测试\img_3.png')

1.2 视频读取

读取视频实质上也是针对一帧帧的图片进行处理。

import cv2
import numpy as np

# 读取视频
video_capture = cv2.VideoCapture("test.mp4")
# 检查是否打开成功
while True:
    success, image = video_capture.read()
    # 未成功打开摄像头或视频读取出错
    if not success or image is None:
        break
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    cv2.imshow("灰度图", gray)
    # waitKey代表处理快慢,10 表示每帧之间的延迟为 10 毫秒。
    if cv2.waitKey(10) & 0xff == 27:
        break
video_capture.release()
cv2.destroyAllWindows()

2、ROI(Region of Interest)区域和图像边界填充

图像截取,截取你感兴趣的区域,在图像处理中,我们经常只对图像的某一部分区域感兴趣,这部分区域就称为 ROI (Region of Interest) 。常用于目标检测、目标跟踪、图像分割等场景。

2.1 通过python切片操作来选取ROI

import cv2

image = cv2.imread("test.png")
# 选取 ROI (例如选取图像左上角 200x200 的区域)
roi_image = image[0:200, 0:200]
# 颜色通道提取
b, g, r = cv2.split(roi_image)
# 显示 ROI
cv2.imshow('ROI_REGION', roi_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

2.2 交互式方法,使用cv2.selectROI() 函数选取 ROI

本质也是切片操作,只是提供了一个交互的动作

import cv2  
  
img = cv2.imread('image.jpg')  
  
# 选择 ROI  
roi_coordinates = cv2.selectROI(img, False, False, True)  
  
# 打印 ROI 的坐标  
print(roi_coordinates)  
  
# 根据 ROI 坐标裁剪图像  
roi_cropped = img[int(roi_coordinates[1]):int(roi_coordinates[1] + roi_coordinates[3]),  
                  int(roi_coordinates[0]):int(roi_coordinates[0] + roi_coordinates[2])]  
  
# 显示裁剪后的 ROI  
cv2.imshow('ROI Cropped', roi_cropped)  
cv2.waitKey(0)  
cv2.destroyAllWindows()

融合后的图片2.png

3、图像的腐蚀与膨胀

图像腐蚀与膨胀是形态学图像处理中的两种基本操作,它们通常用于二值图像(黑白图像),但也可以扩展到灰度图像。这两种操作是通过核(Kernel)与图像进行卷积来实现,能够有效地去除噪声、分割独立元素、连接相邻元素以及寻找图像中的明显边界等。

3.1 图像腐蚀

操作原理:用kernel扫描图像的每一个像素,用kernel与其覆盖的二值图像做“与”操作,如果都为 1,结果图像的该像素为 1,否则为 0。

作用

  • 去掉图片周围的无关项,消除边界点。
  • 去除小且无意义的物体或点。
# 读取图像, 以灰度模式读取图像  
img = cv2.imread('image.jpg', 0)  
  
# 定义kernel大小  
kernel = np.ones((5, 5), np.uint8)  
  
# 图像腐蚀, iterations 表示腐蚀次数  
erosion = cv2.erode(img, kernel, iterations=1)  
  
# 显示结果  
cv2.imshow('Original_img', img)  
cv2.imshow('Erosion_img', erosion)  
cv2.waitKey(0)  
cv2.destroyAllWindows()

融合后的图片.png 左图是原始灰度图,右图是经过腐蚀操作后的图片。很明显原始图片中的一些无关点已被去除,且各物体间的边缘更明显了。

3.2 图像膨胀

操作原理:与腐蚀操作类型,只是kernel与其覆盖的二值图像的操作变为“或”,如果有一个为 1,结果图像的该像素为 1,否则为 0。

作用:

  • 将图像中的高亮区域或白色部分进行扩张。
  • 填补图像中的空洞。
  • 膨胀操作可以降低腐蚀操作带来的影响,例如经过腐蚀后,图片的线条变细了,可以通过膨胀操作将图片线条放大。
import cv2  
import numpy as np  
  
# 读取图像,以灰度模式读取图像  
img = cv2.imread('image.jpg', 0)  
  
# 定义kernel大小  
kernel = np.ones((5, 5), np.uint8)  
  
# 图像膨胀  
dilation = cv2.dilate(img, kernel, iterations=1)  
  
# 显示结果  
cv2.imshow('Original', img)  
cv2.imshow('Dilation', dilation)  
cv2.waitKey(0)  
cv2.destroyAllWindows()

融合后的图片3.png 左图是原始灰度图,右图是经过膨胀操作后的图片。原图中一些很细微的白点被放大了,与此带来的影响是把一些物体边缘细节也会放大。

综上所述,其实腐蚀和膨胀是一对功能相反的操作,两者结合,能够更有针对性的对图片进行处理,如开运算与闭运算。

4.开运算与闭运算

开运算和闭运算是形态学图像处理中基于腐蚀和膨胀的两种重要操作。它们通过组合腐蚀和膨胀操作,能够有效地处理图像中的噪声、连接断裂部分、填补空洞等。下面是两种方法的具体差异。

操作原理作用应用场景
开运算先腐蚀后膨胀去除小物体、平滑边界、断开连接去噪、分离目标、提取形状
闭运算先膨胀后腐蚀填补空洞、连接物体、平滑边界填补断裂、连接目标、去除孔洞

4.1 开运算 (Opening)

import cv2  
import numpy as np  
  
img = cv2.imread('image.jpg', 0)  
kernel = np.ones((5, 5), np.uint8)  
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)  
  
cv2.imshow('Original_Img', img)  
cv2.imshow('Opening_Img', opening)  
cv2.waitKey(0)  
cv2.destroyAllWindows()

融合后的图片4.png 左图是原始灰度图,右图是经过开运算后的图片,很明显可以看出,经过开运算处理后,图片的边缘点消失了,且解决了由于腐蚀操作带来的物体边缘加粗的问题。

4.2 闭运算 (Closing)

import cv2  
import numpy as np  
  
img = cv2.imread('image.jpg', 0)  
kernel = np.ones((5,5), np.uint8)  
  
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)  
  
cv2.imshow('Original', img)  
cv2.imwrite('original.jpg', img)  
cv2.imshow('Closing', closing)  
cv2.imwrite('closing.jpg', closing)  
cv2.waitKey(0)  
cv2.destroyAllWindows()

融合后的图片5.png 左图是原始灰度图,右图是经过闭运算后的图片。如图所示,先经过膨胀放大周围边缘点和细节,再经过腐蚀操作后只会减弱效果膨胀的效果,并不会去掉这些边缘点与细节。