OpenCV(二十):位运算
位运算的概念与基础
在图像处理中,“位运算”(Bitwise Operation)是一类直接作用于像素值二进制位的操作。
OpenCV 对灰度图和彩色图像都支持位运算,常用于掩膜处理、图像融合、背景替换、ROI(感兴趣区域)提取等。
每个像素通常用 8 位(0–255)表示,因此可进行逐位的逻辑计算。
常见的位运算包括:
| 运算类型 | 数学符号 | 功能描述 |
|---|---|---|
| 按位与 | AND (&) | 两个像素同时为 1 时结果为 1,否则为 0 |
| 按位或 | OR (` | `) |
| 按位非 | NOT (~) | 对像素的每一位取反 |
| 按位异或 | XOR (^) | 两个像素的对应位不同则为 1,相同为 0 |
图像的位运算是逐像素逐通道进行的。即对每个像素点 (x, y):
dst(x, y, c) = src1(x, y, c) op src2(x, y, c)
其中 c 表示通道(0、1、2 对应 B、G、R)。
OpenCV 提供的位运算函数
OpenCV 封装了几种常用的位运算函数接口,这些函数均在 cv::core 模块中定义:
| 函数名 | 功能 | 函数原型 |
|---|---|---|
bitwise_and() | 按位与 | bitwise_and(InputArray src1, InputArray src2, OutputArray dst, InputArray mask = noArray()) |
bitwise_or() | 按位或 | bitwise_or(InputArray src1, InputArray src2, OutputArray dst, InputArray mask = noArray()) |
bitwise_xor() | 按位异或 | bitwise_xor(InputArray src1, InputArray src2, OutputArray dst, InputArray mask = noArray()) |
bitwise_not() | 按位取反 | bitwise_not(InputArray src, OutputArray dst, InputArray mask = noArray()) |
1. bitwise_and()
用于提取特定区域或进行掩膜计算。
例:
cv::Mat mask, result;
cv::bitwise_and(img1, img2, result, mask);
含义:result = (img1 & img2) & mask
mask 非零处才计算,其余置零。
2. bitwise_or()
可实现图像叠加或合成。
例:
cv::bitwise_or(img1, img2, dst);
通常用于合并两幅互补区域图像。
3. bitwise_xor()
用于检测差异或制作透明区域。
例:
cv::bitwise_xor(img1, img2, diff);
只有两个图像不同位置会得到高亮。
4. bitwise_not()
图像反色操作:黑变白,白变黑。
cv::bitwise_not(gray, inverted);
在遮罩图像处理中非常常见。
位运算的常见应用场景
1. 掩膜提取(Mask Operation)
掩膜是位运算最常用的场景。
例如从一张图中提取目标区域:
import cv2
import numpy as np# 1. 读取原图
img = cv2.imread('example.jpg') # 请替换为你的图片路径
if img is None:raise FileNotFoundError("无法加载图片,请检查路径")# 2. 创建掩膜(mask),大小与原图相同,初始化为全零(黑色)
mask = np.zeros(img.shape[:2], dtype=np.uint8)# 3. 定义感兴趣区域(ROI),例如一个圆形区域
center = (img.shape[1] // 2, img.shape[0] // 2) # 图像中心
radius = min(img.shape[:2]) // 4
cv2.circle(mask, center, radius, 255, -1) # 白色圆形区域# 4. 通过掩膜提取ROI
result = cv2.bitwise_and(img, img, mask=mask)# 5. 显示结果
cv2.imshow('Original', img)
cv2.imshow('Mask', mask)
cv2.imshow('Result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
这里通过阈值生成二值掩膜,掩膜中白色区域被保留,黑色区域被屏蔽。
2. 背景替换与图像合成
位运算结合掩膜,可以方便地将前景叠加到新背景中。
import cv2
import numpy as np# 读取前景(人物)与背景图
fg = cv2.imread('person_green.jpg')
bg = cv2.imread('new_background.jpg')# 调整背景大小与前景一致
bg = cv2.resize(bg, (fg.shape[1], fg.shape[0]))# 转换到 HSV 空间
hsv = cv2.cvtColor(fg, cv2.COLOR_BGR2HSV)# 定义绿色范围(可根据情况调整)
lower_green = np.array([35, 40, 40])
upper_green = np.array([85, 255, 255])# 生成绿色掩膜
mask = cv2.inRange(hsv, lower_green, upper_green)# 反向掩膜(保留非绿区域)
mask_inv = cv2.bitwise_not(mask)# 提取前景中非绿色部分
fg_part = cv2.bitwise_and(fg, fg, mask=mask_inv)# 提取背景中对应绿色部分
bg_part = cv2.bitwise_and(bg, bg, mask=mask)# 合成新图像
result = cv2.add(fg_part, bg_part)cv2.imshow('Foreground', fg)
cv2.imshow('Background', bg)
cv2.imshow('Mask', mask)
cv2.imshow('Result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
这种方式广泛用于虚拟背景、更换人像背景、Logo 叠加等场景。
3. 图像差异检测(运动检测)
按位异或(XOR)常用于检测两帧之间的变化:
import cv2
import numpy as np# 打开视频或摄像头
cap = cv2.VideoCapture('video.mp4') # 或 0 使用摄像头ret, prev_frame = cap.read()
prev_gray = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY)while True:ret, frame = cap.read()if not ret:breakgray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)# 计算相邻帧差异diff = cv2.absdiff(prev_gray, gray)# 二值化_, thresh = cv2.threshold(diff, 25, 255, cv2.THRESH_BINARY)# 形态学去噪kernel = np.ones((5, 5), np.uint8)thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)thresh = cv2.morphologyEx(thresh, cv2.MORPH_DILATE, kernel)# 找运动轮廓contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)for c in contours:if cv2.contourArea(c) < 500:continuex, y, w, h = cv2.boundingRect(c)cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)cv2.imshow('Frame', frame)cv2.imshow('Diff', diff)cv2.imshow('Motion Mask', thresh)prev_gray = gray.copy()if cv2.waitKey(30) & 0xFF == 27: # 按 ESC 退出breakcap.release()
cv2.destroyAllWindows()
通过异或后再阈值化,就能获取运动区域。
4. ROI(感兴趣区域)提取
ROI 提取常结合掩膜和按位与使用:
import cv2
import numpy as np# 读取原图
img = cv2.imread('image.jpg')# 创建一个与原图大小一致的黑色掩膜
mask = np.zeros(img.shape[:2], np.uint8)# 定义 ROI 区域 (x, y, w, h)
x, y, w, h = 100, 100, 200, 150
mask[y:y+h, x:x+w] = 255 # 在掩膜上标记 ROI 区域为白色# 用掩膜提取 ROI
roi = cv2.bitwise_and(img, img, mask=mask)cv2.imshow('Original', img)
cv2.imshow('Mask', mask)
cv2.imshow('ROI Extracted', roi)
cv2.waitKey(0)
cv2.destroyAllWindows()
这样仅保留矩形区域内的像素,其余区域变为黑色。
5. 图像融合与透明叠加
位运算可用于图像融合前的预处理,例如使用 bitwise_and 控制叠加区域,addWeighted() 实现平滑融合:
import cv2
import numpy as np# 读取背景图与 logo
bg = cv2.imread('background.jpg')
logo = cv2.imread('logo.png')# 调整 logo 尺寸
rows, cols = logo.shape[:2]
roi = bg[0:rows, 0:cols] # 选取背景左上角区域# 转换为灰度图,用于制作掩膜
logo_gray = cv2.cvtColor(logo, cv2.COLOR_BGR2GRAY)# 生成掩膜和反掩膜
_, mask = cv2.threshold(logo_gray, 10, 255, cv2.THRESH_BINARY)
mask_inv = cv2.bitwise_not(mask)# 从背景中抠出 ROI 区域(mask_inv=黑背景)
bg_part = cv2.bitwise_and(roi, roi, mask=mask_inv)# 从 logo 中抠出前景
logo_part = cv2.bitwise_and(logo, logo, mask=mask)# 融合两部分
dst = cv2.add(bg_part, logo_part)# 放回原图
bg[0:rows, 0:cols] = dstcv2.imshow('Mask', mask)
cv2.imshow('Result', bg)
cv2.waitKey(0)
cv2.destroyAllWindows()
这种做法比直接相加更精确,可以控制透明区域。
总结
OpenCV 的位运算是图像处理的基础工具之一,其优势在于:
- 直接操作像素二进制,速度快、无精度损失;
- 结合掩膜灵活控制区域;
- 在图像合成、抠图、分割中广泛使用;
- 支持硬件加速,可满足实时视频场景。
常见模式总结如下:
| 应用 | 函数 | 说明 |
|---|---|---|
| 提取目标区域 | bitwise_and | 保留 mask 白区 |
| 背景替换 | bitwise_not + bitwise_and + add | 前景/背景分离 |
| 差异检测 | bitwise_xor | 检测运动/变化 |
| 图像反色 | bitwise_not | 图像反相 |
| 局部ROI处理 | 任意位运算 + mask | 精准控制计算范围 |
