小白从零开始勇闯人工智能:计算机视觉初级篇(初识Opencv)

引言

在人工智能发展的过程中,计算机视觉也开始更频繁的进入到大众的视野里。作为该领域最主流的开源工具库,OpenCV如同一把功能强大的钥匙,开启了探索图像识别与分析的大门。

一、环境搭建与初体验

1、安装OpenCV

在开始之前,我们需要准备好OpenCV。

复制代码
# 安装OpenCV基础库
pip install opencv-python==3.4.18.65 -i https://pypi.tuna.tsinghua.edu.cn/simple

# 安装OpenCV扩展库(包含更多算法)
pip install opencv-contrib-python==3.4.18.65 -i https://pypi.tuna.tsinghua.edu.cn/simple

2、第一行代码:读取和显示图像

首先,我们需要导入cv2模块,然后通过cv2.imread函数从指定路径加载名为'R-C.jpg'的图像文件。随后,cv2.imshow函数会创建一个标题为'My First OpenCV Image'的窗口来展示该图像。为了保持窗口开启,cv2.waitKey(0)会暂停程序,直到按下任意按键。最后,cv2.destroyAllWindows确保所有已创建的图像窗口被正确关闭,完成整个流程。

复制代码
import cv2

# 读取图像
image = cv2.imread('R-C.jpg')
# 显示图像
cv2.imshow('My First OpenCV Image', image)
# 等待按键
cv2.waitKey(0)
# 关闭所有窗口
cv2.destroyAllWindows()

3、了解图像的基本属性

在计算机视觉中,图像本质上是一个由像素值构成的数字矩阵。通过OpenCV读取图像后,我们可以查看其关键属性:image.shape输出图像的维度信息,通常为(高度, 宽度, 通道数),例如彩色BGR图像有三个通道,image.dtype显示数据的存储类型,通常是uint8,代表每个像素值范围在0到255之间,image.size则给出图像的总像素数量,即高度、宽度与通道数的乘积。这些基本属性是我们理解和进行后续图像处理操作的重要基础。

二、图像基础操作

1、灰度图像处理

将彩色图像读取为灰度图方法是使用语义化常量 cv2.IMREAD_GRAYSCALE,它会使cv2.imread函数在加载图像时,直接将其转换为单通道的灰度图像,从而丢弃所有颜色信息,转换后的图像数据矩阵仅包含每个像素的亮度值。

复制代码
import cv2
gray_image = cv2.imread('R-C.jpg', cv2.IMREAD_GRAYSCALE)

cv2.imshow('Gray Image', gray_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

2、ROI(感兴趣区域)

在图像处理中,ROI(感兴趣区域)是一个核心概念,它允许我们聚焦并仅对图像的特定部分进行操作。通过NumPy数组切片,我们可以轻松提取ROI,其格式为 image[起始Y坐标:结束Y坐标, 起始X坐标:结束X坐标]。例如,代码 roi = image[100:300, 150:350] 便从原始图像中截取了一个高度从第100到300像素、宽度从第150到350像素的矩形区域,随后将其显示在标题为'ROI'的新窗口中。

复制代码
import cv2
image = cv2.imread('R-C.jpg')
# 提取图像的一部分(格式:image[Y_start:Y_end, X_start:X_end])
roi = image[100:300, 150:350]  
cv2.imshow('ROI', roi)
cv2.waitKey(0)

3、颜色通道操作

在OpenCV中,彩色图像通常以BGR顺序的三通道矩阵表示。我们可以通过数组切片或使用cv2.split(image)函数来分离出单独的蓝、绿、红通道。每个单独通道显示为灰度图,代表该颜色的强度。若想突出显示特定颜色,例如红色,可复制原图像并将其蓝色和绿色通道值置零,仅保留红色通道,从而生成一幅视觉上为纯红色的图像。这些操作是进行色彩分析、滤镜应用和复杂图像处理的基础。

复制代码
import cv2
image = cv2.imread('R-C.jpg')
# 分离颜色通道
blue_channel = image[:, :, 0]  # B通道
green_channel = image[:, :, 1]  # G通道
red_channel = image[:, :, 2]  # R通道

# 使用OpenCV函数分离
b, g, r = cv2.split(image)

# 显示红色通道(注意:单独显示一个通道会是灰度图)
cv2.imshow('Red Channel', r)


# 如果要显示真正的红色图像(只保留红色,去除蓝绿)
red_image = image.copy()
red_image[:, :, 0] = 0  # 蓝色通道设为0
red_image[:, :, 1] = 0  # 绿色通道设为0
cv2.imshow('Pure Red Image', red_image)
cv2.waitKey(0)

4、图像基本变换

OpenCV 提供了灵活的几何变换功能。图像缩放可通过 cv2.resize 实现:可以直接指定目标尺寸(如宽300像素、高200像素),或使用缩放因子按比例调整(例如将宽高同时缩小一半),还可以仅调整宽度而保持高度不变。图像旋转则需要先计算图像中心点,然后利用 cv2.getRotationMatrix2D 生成旋转矩阵(例如旋转45度),最后通过 cv2.warpAffine 应用旋转变换。

复制代码
import cv2
image = cv2.imread('R-C.jpg')

#图像缩放
# 方法1:指定目标尺寸
resized1 = cv2.resize(image, (300, 200))  # 宽300,高200
cv2.imshow('image1', resized1)
# 方法2:按比例缩放
resized2 = cv2.resize(image, None, fx=0.5, fy=0.5)  # 宽高都缩小一半
cv2.imshow('image2', resized2)
# 方法3:只缩放宽度
resized3 = cv2.resize(image, None, fx=0.5, fy=1)  # 宽度减半,高度不变
cv2.imshow('image3', resized3)
cv2.waitKey(0)
复制代码
import cv2
image = cv2.imread('R-C.jpg')

#图像旋转
# 获取图像中心点
(h, w) = image.shape[:2]
center = (w // 2, h // 2)

# 创建旋转矩阵
rotation_matrix = cv2.getRotationMatrix2D(center, 45, 1.0)  # 旋转45度

# 应用旋转
rotated = cv2.warpAffine(image, rotation_matrix, (w, h))
cv2.imshow('Rotated Image', rotated)
cv2.waitKey(0)

三、图像运算与融合

1、图像加法

图像加法有两种不同实现方式,其重要区别是:第一种方法是直接使用NumPy的 + 运算符进行加法 (img1 + img2),这种方式遵循模运算,即当像素值之和超过255(uint8类型的上限)时,结果会从0开始重新计算,导致色彩异常。第二种方法是使用OpenCV专有的 cv2.add() 函数,它执行饱和操作,将任何超过255的和直接截断(或"饱和")为最大值255,从而在重叠区域产生更符合视觉预期的明亮色彩。

复制代码
import numpy as np

# 创建两个简单的图像用于演示
img1 = np.zeros((300, 300, 3), dtype=np.uint8)
img1[100:200, 100:200] = [255, 0, 0]  # 蓝色方块

img2 = np.zeros((300, 300, 3), dtype=np.uint8)
img2[150:250, 150:250] = [0, 255, 0]  # 绿色方块

# 方法1:使用+运算符(超出255会取模)
add1 = img1 + img2

# 方法2:使用cv2.add()(超出255会被截断为255)
add2 = cv2.add(img1, img2)

cv2.imshow('Using + operator', add1)
cv2.imshow('Using cv2.add()', add2)
cv2.waitKey(0)
cv2.destroyAllWindows()

2、图像融合(加权加法)

OpenCV中图像融合涵盖了一系列基础的操作:首先通过cv2.imread读取图像,并可利用.shape等属性查看其矩阵结构。图像可被直接转换为灰度图,或通过数组切片提取特定区域(ROI)进行处理。对于彩色图像,可以分离并操作其BGR通道。几何变换包括使用cv2.resize进行缩放,以及通过cv2.getRotationMatrix2D和cv2.warpAffine实现旋转。最后,cv2.addWeighted函数能依据权重系数实现图像间的平滑融合,创造出过渡效果。

复制代码
import cv2
image = cv2.imread('R-C.jpg')
import numpy as np

# 创建两个简单的图像用于演示
img1 = np.zeros((300, 300, 3), dtype=np.uint8)
img1[100:200, 100:200] = [255, 0, 0]  # 蓝色方块


# 确保两张图像大小相同
img2 = cv2.resize(image, (img1.shape[1], img1.shape[0]))

# 图像融合:dst = α·img1 + β·img2 + γ
blended = cv2.addWeighted(img1, 0.7, img2, 0.3, 0)

cv2.imshow('Blended Image', blended)
cv2.waitKey(0)

四、图像边界处理

在OpenCV中,cv2.copyMakeBorder函数为图像边缘处理提供了多种边界填充策略。常数填充(BORDER_CONSTANT)使用指定颜色(如绿色[0,255,0])扩展画布。反射填充(BORDER_REFLECT)通过镜像图像边缘的像素来创建平滑过渡。边缘复制(BORDER_REPLICATE)则直接复制并延伸图像最外侧的像素行和列。这些方法在处理卷积、滤波等操作时至关重要,能有效管理边界区域,确保图像尺寸变化并适应不同算法的需求。

复制代码
import cv2
image = cv2.imread('R-C.jpg')


# 五种边界填充方式演示
top, bottom, left, right = 50, 50, 50, 50

# 1. 常数填充(填充指定颜色)
constant = cv2.copyMakeBorder(image, top, bottom, left, right,
                              cv2.BORDER_CONSTANT, value=[0, 255, 0])

# 2. 反射填充(镜像反射边界)
reflect = cv2.copyMakeBorder(image, top, bottom, left, right,
                             cv2.BORDER_REFLECT)

# 3. 边缘复制(复制最边缘的像素)
replicate = cv2.copyMakeBorder(image, top, bottom, left, right,
                               cv2.BORDER_REPLICATE)

# 显示结果
cv2.imshow('Constant Border', constant)
cv2.imshow('Reflect Border', reflect)
cv2.imshow('Replicate Border', replicate)
cv2.waitKey(0)

五、阈值处理------图像二值化的艺术

阈值处理是图像分割的一项基础技术,其核心是通过设定一个或多个阈值来对图像的像素灰度值进行判断与分类,从而简化图像信息。以OpenCV的cv2.threshold函数为例,它将灰度图像中高于阈值的像素点置为白色(255),低于阈值的置为黑色(0),直接生成非黑即白的二值图像。这种方法能有效分离目标与背景,是后续进行轮廓检测、特征提取等高级分析的关键预处理步骤。

复制代码
import cv2
image = cv2.imread('R-C.jpg')


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

# 五种阈值处理方法
_, binary = cv2.threshold(gray, 175, 255, cv2.THRESH_BINARY)          # 二值化
_, binary_inv = cv2.threshold(gray, 175, 255, cv2.THRESH_BINARY_INV)  # 反二值化
_, trunc = cv2.threshold(gray, 175, 255, cv2.THRESH_TRUNC)           # 截断
_, tozero = cv2.threshold(gray, 175, 255, cv2.THRESH_TOZERO)         # 零处理
_, tozero_inv = cv2.threshold(gray, 175, 255, cv2.THRESH_TOZERO_INV) # 反零处理

# 显示结果
titles = ['Original', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [gray, binary, binary_inv, trunc, tozero, tozero_inv]


cv2.imshow(titles[0], images[0])
cv2.imshow(titles[1], images[1])
cv2.imshow(titles[2], images[2])
cv2.imshow(titles[3], images[3])
cv2.imshow(titles[4], images[4])
cv2.imshow(titles[5], images[5])

cv2.waitKey(0)
cv2.destroyAllWindows()

阈值处理类型对比表:

复制代码
类型	                说明	                        公式
THRESH_BINARY	    大于阈值设为最大值,否则为0	dst(x,y) = maxval if src(x,y)>thresh else 0
THRESH_BINARY_INV	与BINARY相反                	dst(x,y) = 0 if src(x,y)>thresh else maxval
THRESH_TRUNC	    大于阈值设为阈值,否则不变     dst(x,y) = thresh if src(x,y)>thresh else src(x,y)
THRESH_TOZERO	    大于阈值不变,否则为0	      dst(x,y) = src(x,y) if src(x,y)>thresh else 0
THRESH_TOZERO_INV	与TOZERO相反	                dst(x,y) = 0 if src(x,y)>thresh else src(x,y)

六、图像平滑处理------消除噪声

1、理解图像噪声

在真实世界中,图像采集和传输过程常常会引入各种噪声干扰,例如高斯噪声、椒盐噪声等,这些噪声会降低图像质量并影响后续处理效果。为了有效抑制噪声,OpenCV提供了多种滤波函数:cv2.GaussianBlur使用高斯核对图像进行平滑,适用于消除高斯噪声,cv2.medianBlur采用中值滤波,能有效去除椒盐噪声且更好地保留边缘,而cv2.bilateralFilter作为一种保边滤波器,在平滑的同时能较好地保持图像轮廓的清晰度。

我们先创建一个带噪声的图像:

复制代码
import numpy as np
import cv2

def add_salt_pepper_noise(image, salt_prob=0.01, pepper_prob=0.01):
    """添加椒盐噪声"""
    noisy = image.copy()
    total_pixels = image.size

    # 添加盐噪声(白点)
    num_salt = int(total_pixels * salt_prob)
    salt_coords = [np.random.randint(0, i - 1, num_salt) for i in image.shape]
    noisy[salt_coords[0], salt_coords[1]] = 255

    # 添加胡椒噪声(黑点)
    num_pepper = int(total_pixels * pepper_prob)
    pepper_coords = [np.random.randint(0, i - 1, num_pepper) for i in image.shape]
    noisy[pepper_coords[0], pepper_coords[1]] = 0

    return noisy


# 创建测试图像
test_image = cv2.imread('R-C.jpg', 0)
noisy_image = add_salt_pepper_noise(test_image)
cv2.imshow('noisy_image', noisy_image)
cv2.waitKey(0)

2、均值滤波(Mean Filtering)

均值滤波是最基础的线性平滑方法,其核心原理是将图像中每个像素点的值替换为其周围矩形邻域内所有像素值的算术平均值。这种方法能有效降低图像中的随机噪声,使图像变得模糊和平滑。在OpenCV中,可通过cv2.blur()或cv2.boxFilter()函数实现。然而,它在消除噪声的同时也会不可避免地模糊图像的边缘与细节,因为它平等地对待邻域内的所有像素,无法区分噪声与重要的图像特征。

复制代码
# 应用3×3均值滤波
mean_filtered = cv2.blur(noisy_image, (3, 3))

# 应用5×5均值滤波(效果更平滑但更模糊)
mean_filtered_large = cv2.blur(noisy_image, (5, 5))

3、高斯滤波(Gaussian Filtering)

高斯滤波是一种更高级的线性平滑技术,它通过引入一个基于二维高斯函数的核(或称滤波器)来考虑距离因素。与均值滤波对所有邻域像素赋予相同权重不同,高斯核的中心像素具有最高权重,权重值随着与中心距离的增加呈高斯分布(钟形曲线)递减。这意味着距离中心越近的像素对最终结果的影响越大,距离越远的像素影响则越小。这种方法实现的平滑效果更为自然,能在有效抑制噪声(尤其是高斯噪声)的同时,比均值滤波更好地保留图像的总体边缘结构和信号特征。在OpenCV中,这一操作通过 cv2.GaussianBlur() 函数实现。

复制代码
# 应用高斯滤波
gaussian_filtered = cv2.GaussianBlur(noisy_image, (5, 5), 0)

4、中值滤波(Median Filtering)

中值滤波是一种非常有效的非线性去噪方法,它特别擅长去除椒盐噪声(即随机出现的黑白噪点)。其工作原理是将每个像素点替换为其邻域内所有像素值的中值,而非平均值。由于中值对极值不敏感,因此孤立的、亮度差异极大的噪声点(椒盐噪声的典型特征)在排序后很容易被边缘的正常像素值所取代,从而被直接过滤掉。同时,这种基于排序的替换方式能更好地保留图像的锐利边缘,因为边缘的像素值不会被周围像素的平均值所模糊。在OpenCV中,这一操作通过 cv2.medianBlur() 函数实现。

复制代码
# 应用中值滤波
median_filtered = cv2.medianBlur(noisy_image, 5)

5、方框滤波(Box Filtering)

方框滤波是均值滤波的通用形式,其核心区别在于是否进行归一化。当选择归一化时:滤波器核内所有像素的权重相等,其效果与均值滤波完全相同。当选择不归一化时:滤波器直接计算邻域内所有像素值的总和。这会导致输出像素值可能远超正常范围(例如高达数千),造成图像异常明亮甚至全白(溢出后被截断)。虽然这通常不是期望的平滑效果,但在某些特定的底层计算或需要求和的场景中可能有用。因此,均值滤波可以看作是方框滤波在开启归一化选项时的一个特例。

复制代码
# 归一化的方框滤波(与均值滤波相同)
box_filtered_norm = cv2.boxFilter(noisy_image, -1, (3, 3), normalize=True)

# 非归一化的方框滤波(直接求和,可能溢出)
box_filtered_non_norm = cv2.boxFilter(noisy_image, -1, (3, 3), normalize=False)

6、滤波效果对比

复制代码
import matplotlib.pyplot as plt

# 应用不同滤波器
filters = {
    "Original": noisy_image,
    "Mean (3x3)": cv2.blur(noisy_image, (3, 3)),
    "Gaussian (5x5)": cv2.GaussianBlur(noisy_image, (5, 5), 0),
    "Median (5x5)": cv2.medianBlur(noisy_image, 5),
}

# 显示结果
plt.figure(figsize=(12, 8))
for i, (name, img) in enumerate(filters.items(), 1):
    plt.subplot(2, 2, i)
    plt.imshow(img, cmap='gray')
    plt.title(name)
    plt.axis('off')
plt.tight_layout()
plt.show()

到这里我们就对计算机视觉有了一个基础的了解,在后续的文章里我们将学习更多有关计算机视觉的知识,如图像形态学等等。

相关推荐
NAGNIP4 小时前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
冬奇Lab5 小时前
一天一个开源项目(第36篇):EverMemOS - 跨 LLM 与平台的长时记忆 OS,让 Agent 会记忆更会推理
人工智能·开源·资讯
冬奇Lab5 小时前
OpenClaw 源码深度解析(一):Gateway——为什么需要一个"中枢"
人工智能·开源·源码阅读
AngelPP9 小时前
OpenClaw 架构深度解析:如何把 AI 助手搬到你的个人设备上
人工智能
宅小年9 小时前
Claude Code 换成了Kimi K2.5后,我再也回不去了
人工智能·ai编程·claude
九狼9 小时前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
ZFSS9 小时前
Kimi Chat Completion API 申请及使用
前端·人工智能
天翼云开发者社区10 小时前
春节复工福利就位!天翼云息壤2500万Tokens免费送,全品类大模型一键畅玩!
人工智能·算力服务·息壤
知识浅谈10 小时前
教你如何用 Gemini 将课本图片一键转为精美 PPT
人工智能
Ray Liang11 小时前
被低估的量化版模型,小身材也能干大事
人工智能·ai·ai助手·mindx