倾情奉献 , Python 的 OpenCV 常见操作指南

2,370 阅读10分钟

👈👈👈 欢迎点赞收藏关注哟 👍👍👍

👉👉👉 快进来看爆款文章 🔥🔥🔥

一. 前言

上一篇我们了解了 计算机视觉的基础概念 , 那么这一篇就继续来深入应用 :

对于普通的开发者来说 ,特征工程 ,算法变换这些还太远。就应用角度来说 ,多数只是调用工具实现需求 ,而工具这一块最有名的无非就是 OpenCV 了。

通常一个简单的图形读取分为几步?

二. 基础入门

2.1 OpenCV 读取图像

  • 语法 retval = cv2.imread( filename[, flags] )
    • retval : 未读取到图像是 None
    • flags : 读取标记 , 支持正常,灰度,BGR 图像
# 读取彩色图像(默认方式)
image = cv2.imread(image_path)

# 读取灰度图像
gray_image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)

# 读取图像时保持 alpha 通道
alpha_image = cv2.imread(image_path, cv2.IMREAD_UNCHANGED)

2.2 OpenCV 显示图像

  • 语法cv2.imshow( winname, mat )
    • winname : 窗口名称
    • mat : 要显示的图像
// 显示图像
cv2.imshow('Image', image)

关联附加操作 :等待按键确定

当用户按下按键后,该语句会被执行,并获取返回值 。 如果在上述代码中不使用该代码 ,则图片显示会瞬间完成。

通过该代码可以实现暂停功能

  • 语法retval = cv2.waitKey( [delay] )
    • delay : 等待时间 。 默认为0 ,可以为正数

// 持续暂停等待按键确认
// - 无限期等待键盘事件。当任何键被按下时,程序继续执行
cv2.imshow('Image', image)
cv2.waitKey(0)

// - 等待 n 毫秒的键盘事件
// - 如果在指定时间内有键按下,程序继续执行并返回按键的 ASCII 码
// - 如果没有键按下,返回 -1
cv2.waitKey(3000)


// 通常可以返回处理的按键 : 
if key == ord('q'):
    print("按下了 'q' 键")
elif key == ord('s'):
    cv2.imwrite('saved_image.jpg', image)
    print("按下了 's' 键,图像已保存为 'saved_image.jpg'。")

关联操作 : 关闭窗口

 cv2.destroyAllWindows( )

2.3 保存图像

  • 语法 : retval = cv2.imwrite( filename, img[, params] )
    • filename : 保存的完整路径名
    • img : 被保存的图像
    • param : 可选参数 ,支持
      • cv2.IMWRITE_JPEG_QUALITY: 指定图片质量 (JPG)
      • cv2.IMWRITE_PNG_COMPRESSION : 指定压缩级别(PNG)

// 正常保存
cv2.imwrite('output_image.jpg', image)

// 图像质量,值范围从 0100(默认值为 95)
cv2.imwrite('output_image.jpg', image, [cv2.IMWRITE_JPEG_QUALITY, 90])

// 压缩级别,值范围从 09(默认值为 3)
cv2.imwrite('output_image.png', image, [cv2.IMWRITE_PNG_COMPRESSION, 5])

2.4 其他的图像基本操作

尺寸操作

// 获取图像的宽高并且打印
height, width = image.shape[:2]
print(f'{height} x {width}')


// 调整尺寸
resized_image = cv2.resize(image, (width, height))

# 按照比例缩放图片
resized_image = cv2.resize(image, None, fx=0.5, fy=0.5)

# 按照比例扩展图片
resized_image = cv2.resize(image, None, fx=2.5, fy=2.5)

# 按照尺寸调整图片
scale_factor = 30  # 放大倍数
new_height = height * scale_factor
new_width = width * scale_factor
resized_image = cv2.resize(image, (new_width, new_height), interpolation=cv2.INTER_NEAREST)

## interpolation:插值方法,用于重采样图像。常见的插值方法包括:
cv2.INTER_NEAREST:最近邻插值
cv2.INTER_LINEAR:双线性插值(默认值)
cv2.INTER_AREA:使用像素区域关系进行重采样。对于缩小图像非常有效
cv2.INTER_CUBIC:4x4 像素邻域的双三次插值
cv2.INTER_LANCZOS4:8x8 像素邻域的 Lanczos 插值

旋转操作

# 图像旋转
center = (width / 2, height / 2)
M = cv2.getRotationMatrix2D(center, 45, 1.0)
new_image = cv2.warpAffine(image, M, (width, height))


# getRotationMatrix2D : 用于生成二维旋转矩阵
cv2.getRotationMatrix2D(center, angle, scale)
- center:旋转中心点,格式为 (x, y)
- angle:逆时针旋转角度,以度为单位
- scale:缩放因子


# warpAffine :  用于对图像进行仿射变换
cv2.warpAffine(src, M, dsize, dst=None, flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_CONSTANT, borderValue=0)
- src:输入图像
- M:变换矩阵,通常由 cv2.getRotationMatrix2D 生成
- dsize:输出图像的大小,格式为 (width, height)
- dst:输出图像。这个参数通常省略,直接接收返回值
- flags:插值方法。常用的有 cv2.INTER_LINEAR、cv2.INTER_NEAREST 等
- borderMode:边界像素模式,默认值为 cv2.BORDER_CONSTANT
- borderValue:边界像素填充值,当 borderMode 为 cv2.BORDER_CONSTANT 时有效

翻转操作

# 垂直翻转
flipped_image = cv2.flip(image, 0)

# 水平翻转
flipped_image = cv2.flip(image, 1)

# 同时进行垂直和水平翻转
flipped_image = cv2.flip(image, -1)

当然除了这些常见的操作 ,还有一些高级操作,例如模糊锐化这种,我们在后续高阶用法中综合展示!!

三. 高阶用法

3.1 像素处理

像素处理在上一篇也简单聊过 ,这里就再深入一下 :

就像上文 基础概念 里面讲的一样,图像是由一个个像素点组成的 ,最常见的像素操作分为以下几类 :

3.1.1 像素的定位和索引

  • 这个阶段就需要引入 numpy 来进行一些数组的操作了 ,第一步为每个图像像素点生成一个二维数组

image.png

import cv2
import numpy as np

# 读取图像
image_path = "C:\\Users\\Desktop\\test.png"
image = cv2.imread(image_path)

gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 获取灰度值
height, width = gray_image.shape
scale_factor = 100  # 放大倍数
new_height = height * scale_factor
new_width = width * scale_factor

# 放大图像
resized_image = cv2.resize(gray_image, (new_width, new_height), interpolation=cv2.INTER_NEAREST)

# 在放大后的图像上标注像素点
for i in range(height):
    for j in range(width):
        gray_value = gray_image[i, j]
        text = f'({i},{j})'
        # 在放大后的像素区域的中心位置绘制像素值
        x = j * scale_factor + scale_factor // 4
        y = i * scale_factor + scale_factor // 2

        inverted_color = 255 - int(gray_value)
        cv2.putText(resized_image, text, (x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.3, inverted_color, 1, cv2.LINE_AA)

# 显示新图像
cv2.imshow("Gray Image with Values", resized_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 保存新图像
cv2.imwrite("test001.png", resized_image)
  • 该段代码还可以用于展示灰度值 / RGB 值等多项操作
  • 当我们了解到这些值后 ,就可以对数据进行筛选和处理了

3.1.2 像素区域的查询及操作

查询的目的是为了我们把想要的像素点筛选出来 ,通过各种算法的手法对像素点进行处理 :

案例一 : 切换图片底色

import cv2
import numpy as np

# 读取图像
image = cv2.imread(image_path)

# 检查图像是否成功读取
if image is None:
    print("图像读取失败,请检查文件路径。")
else:
    # 定义白色的阈值
    lower_white = np.array([200, 200, 200], dtype=np.uint8)
    upper_white = np.array([255, 255, 255], dtype=np.uint8)

    # 创建掩膜,找到白色部分
    mask = cv2.inRange(image, lower_white, upper_white)

    # 将白色部分替换为灰色
    image[mask == 255] = [128, 128, 128]

    # 显示处理后的图像
    cv2.imshow('Processed Image', image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    # 保存处理后的图像
    cv2.imwrite('processed_image.jpg', image)

这里说白了就是对不同像素点的操作 ,提高像素点的颜色范围

案例二 : 裁剪特定颜色图片

import cv2
import numpy as np

def remove_red_area(image_path):
    # 读取图像
    image = cv2.imread(image_path)
    
    # 将图像从 BGR 转换为 HSV
    hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    
    # 定义红色的 HSV 范围
    lower_red1 = np.array([0, 70, 50])
    upper_red1 = np.array([10, 255, 255])
    lower_red2 = np.array([170, 70, 50])
    upper_red2 = np.array([180, 255, 255])
    
    # 创建红色掩码
    mask1 = cv2.inRange(hsv_image, lower_red1, upper_red1)
    mask2 = cv2.inRange(hsv_image, lower_red2, upper_red2)
    red_mask = cv2.bitwise_or(mask1, mask2)
    
    # 将红色区域变为白色
    image[red_mask != 0] = [255, 255, 255]
    
    # 显示结果
    cv2.imshow('Image with Red Area Removed', image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    # 保存结果
    cv2.imwrite('image_without_red.jpg', image)
    
    return image

# 使用函数移除红色区域
image_path = "C:\\Users\\Desktop\\test.png"
remove_red_area(image_path)

本质上还是进行了搜索和替换。

3.2 美化处理

美颜处理本质上是对整体图片色调的处理,高级的学不来 ,只能玩点简单的 ,常见的有以下几种 :

  • 线性变换:通过对图像像素值进行线性变换来调整对比度和亮度。
  • 直方图均衡化:通过调整图像的直方图来增强对比度。
import cv2
import numpy as np

def adjust_contrast(image_path, alpha, beta):
    # 读取图像
    image = cv2.imread(image_path)
    
    # 调整对比度和亮度
    adjusted_image = cv2.convertScaleAbs(image, alpha=alpha, beta=beta)
    
    # 显示结果
    cv2.imshow('Original Image', image)
    cv2.imshow('Adjusted Image', adjusted_image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    # 保存结果
    cv2.imwrite('adjusted_image.jpg', adjusted_image)
    
    return adjusted_image

# 使用函数调整对比度(例如:对比度增加1.5倍,亮度增加50)
adjust_contrast('path/to/your/image.jpg', alpha=1.5, beta=50)

image.png

更高级的操作这里就不尝试了 ,后续单章深入了解下。

3.3 水印处理

import cv2
import os
import numpy as np

# 对图像进行旋转
def create_rotated_text_image(text, font, font_scale, thickness, color, angle):

    # 获取文本尺寸和基线
    # font_scale :字体比例因子
    # thickness : 文本线条的粗细(以像素为单位)
    (text_w, text_h), baseline = cv2.getTextSize(text, font, font_scale, thickness)

    # 创建一个包含文本的透明图像
    text_img = np.zeros((text_h + baseline, text_w, 4), dtype='uint8')
    cv2.putText(text_img, text, (0, text_h), font, font_scale, color + (255,), thickness, cv2.LINE_AA)

    # 计算旋转矩阵
    center = (text_w // 2, text_h // 2)
    M = cv2.getRotationMatrix2D(center, angle, 1)

    # 旋转文字图像
    rotated = cv2.warpAffine(text_img, M, (text_w, text_h + baseline), flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_CONSTANT, borderValue=(0,0,0,0))

    return rotated

# 为图像添加水印
def add_watermark(image, text, font=cv2.FONT_HERSHEY_SIMPLEX, font_scale=1, thickness=2, color=(255, 255, 255), alpha=0.5, angle=45, spacing=100):
    # 获取图像尺寸
    h, w = image.shape[:2]

    # 创建一个透明背景
    overlay = np.zeros((h, w, 4), dtype='uint8')
    
    # 创建旋转的文字图像
    rotated_text_img = create_rotated_text_image(text, font, font_scale, thickness, color, angle)
    rotated_h, rotated_w = rotated_text_img.shape[:2]

    # 在透明背景上绘制倾斜的文字
    for y in range(0, h, rotated_h + spacing):
        for x in range(0, w, rotated_w + spacing):
            # 确保水印位置不会超出图像边界
            if y + rotated_h <= h and x + rotated_w <= w:
                overlay[y:y+rotated_h, x:x+rotated_w] = rotated_text_img

    # 将透明背景与原始图像合并
    image_bgra = cv2.cvtColor(image, cv2.COLOR_BGR2BGRA)
    
    # 用于图像加权融合的函数。它可以将两张图像按指定的权重进行融合,从而实现图像混合、叠加
    # alpha : 透明度
    cv2.addWeighted(overlay, alpha, image_bgra, 1 - alpha, 0, image_bgra)

    # 转回 BGR
    image_result = cv2.cvtColor(image_bgra, cv2.COLOR_BGRA2BGR)

    return image_result



# 读取图片
image_path = "C:\\Users\\Desktop\\test003.png"
# 检查文件是否存在
if not os.path.isfile(image_path):
    print("错误:图像文件未找到!")
    exit()

image = cv2.imread(image_path)

# 添加水印
watermarked_image = add_watermark(image, 'AntBlack', font_scale=2, thickness=1, color=(255, 255, 255), alpha=0.3, angle=20, spacing=200)

cv2.imshow("Gray Image with Values", watermarked_image)
cv2.waitKey(0)

image.png

这其中用到了以下几个函数

S1 : 获取一个水印图像

首先要拿到这个水印的长宽高及偏移 ,然后基于这些信息生成一个透明图像 ,并且把水印文字放在图像中

# 获取给定文本字符串在特定字体和比例下的尺寸的函数。该函数返回文本的宽度、高度和基线偏移量
(text_w, text_h), baseline = cv2.getTextSize(text, font, font_scale, thickness)
- text:要绘制的文本字符串。
- fontFace:字体类型
- fontScale:字体比例因子。
- thickness:文本线条的粗细(以像素为单位

# 返回值 : 
- size:包含文本宽度和高度的元组 (width, height)。
- baseline:基线的偏移量(即从文本的底部到基线的距离)

# 创建一个透明的图像 , 通过 putText 为其赋值
text_img = np.zeros((text_h + baseline, text_w, 4), dtype='uint8')
cv2.putText(text_img, text, (0, text_h), font, font_scale, color + (255,), thickness, cv2.LINE_AA)

- dtype='uint8' 指定数组元素的数据类型为无符号8位整数 ,基于颜色通道 ,这里就会生成透明图像

S2 : 进行图像的整合

// 这个函数通过加权的方式将两张图像进行线性混合,可以用来实现图像叠加、融合效果等
cv2.addWeighted(src1, alpha, src2, beta, gamma[, dst[, dtype]]) -> dst
- src1:第一张输入图像
- alpha:第一张图像的权重系数
- src2:第二张输入图像
- beta:第二张图像的权重系数
- gamma:加到加权和上的标量值(通常为0)
- dst:输出图像(可选)
- dtype:输出图像的类型(可选)

3.4 文字识别

@ 五分钟,零基础也能入门 Python 图像文字识别

之前这篇文章写过一部分 ,这里就不详细说了,主要是基于 tesseract , 对 OpenCV 的使用反而不多

总结

主要是一些基础的应用 ,从这些案例上 ,还可以继续深入 ,涉及其他的场景。

参考文档 :

  • 计算机视觉40例 : 从入门到深度学习
  • ChatGPT