目录
[1.1 cv::min()](#1.1 cv::min())
[1.2. cv::max()](#1.2. cv::max())
[1.3 典型的用途](#1.3 典型的用途)
[1.3.2 图像蒙版与取区域提](#1.3.2 图像蒙版与取区域提)
[1.3.3 多帧图像降噪](#1.3.3 多帧图像降噪)
[1.3.4 图像对比与差异检测](#1.3.4 图像对比与差异检测)
[1.4 简单应用举例](#1.4 简单应用举例)
[1.4.1 对图片噪声的抑制](#1.4.1 对图片噪声的抑制)
[1.4.2 图片亮度调整](#1.4.2 图片亮度调整)
[1.4.3 亮区提取](#1.4.3 亮区提取)
[2.1cv::minMaxLoc() 定义](#2.1cv::minMaxLoc() 定义)
[2.2 典型用途](#2.2 典型用途)
[3.2.1 bitwise_not(按位取反 / 反色)](#3.2.1 bitwise_not(按位取反 / 反色))
[3.2.2 bitwise_and(按位与)](#3.2.2 bitwise_and(按位与))
[3.2.3 bitwise_or(按位或)](#3.2.3 bitwise_or(按位或))
[3.2.4. bitwise_xor(按位异或)](#3.2.4. bitwise_xor(按位异或))
[3.3 简单应用举例](#3.3 简单应用举例)
[3.3.1 抠图提取图片的亮区或者暗区](#3.3.1 抠图提取图片的亮区或者暗区)
[3.3.2 图片简单添加水印](#3.3.2 图片简单添加水印)
在实际应用中图像由于拍摄环境等等原因会导致图像细节不清晰,对于连续多次抓拍的图片可以利用不同图片不同之处进行细节互补从而达到图像增强的目的。
一、逐像素比较函数
1.1 cv::min()
void cv::min(InputArray src1, InputArray src2, OutputArray dst);
- 作用 :对两个输入图像
src1和src2进行逐像素比较,在每个位置取较小 的像素值,输出到dst。 - 要求 :
src1和src2必须尺寸、通道数、数据类型完全一致。 - 结果 :
dst与输入图像尺寸、通道数、类型相同,每个像素是对应位置两图中较小的值。
1.2. cv::max()
void cv::max(InputArray src1, InputArray src2, OutputArray dst);
- 作用 :对两个输入图像
src1和src2进行逐像素比较,在每个位置取较大 的像素值,输出到dst。 - 要求 :与
min()相同,src1和src2必须尺寸、通道数、数据类型完全一致。 - 结果 :
dst与输入图像尺寸、通道数、类型相同,每个像素是对应位置两图中较大的值。
1.3 典型的用途
1.3.1图像融合与增强
max():常用于 "亮部融合",将两张曝光不同的照片融合,保留更亮的细节(如夜景 + 补光)。
min():常用于 "暗部融合",保留更暗的细节,或用于图像去噪(取多次拍摄的最小值)。
1.3.2 图像蒙版与取区域提
与阈值图像比较,提取亮区或暗区,例如 max(img, threshold_img) 可以保留亮于阈值的区域,暗于阈值的区域被阈值覆盖。
1.3.3 多帧图像降噪
对同一目标拍摄多张图像,取 min() 或 max() 可以有效抑制随机噪声。
1.3.4 图像对比与差异检测
通过 max(img1 - img2, img2 - img1) 可以得到两幅图像的差异图,用于变化检测。
1.4 简单应用举例
1.4.1 对图片噪声的抑制
cpp
// 生成椒盐噪声图像
Mat addSaltPepperNoise(const Mat& src, float noiseRatio = 0.02) {
Mat noisyImg = src.clone();
int noiseNum = src.total() * noiseRatio; // 噪声像素数量
// 添加盐噪声(亮噪声,像素值255)
for (int i = 0; i < noiseNum / 2; i++) {
int x = rand() % src.cols;
int y = rand() % src.rows;
if (src.channels() == 1) { // 灰度图
noisyImg.at<uchar>(y, x) = 255;
}
else { // 彩色图(BGR)
noisyImg.at<Vec3b>(y, x)[0] = 255;
noisyImg.at<Vec3b>(y, x)[1] = 255;
noisyImg.at<Vec3b>(y, x)[2] = 255;
}
}
// 添加椒噪声(暗噪声,像素值0)
for (int i = 0; i < noiseNum / 2; i++) {
int x = rand() % src.cols;
int y = rand() % src.rows;
if (src.channels() == 1) {
noisyImg.at<uchar>(y, x) = 0;
}
else {
noisyImg.at<Vec3b>(y, x)[0] = 0;
noisyImg.at<Vec3b>(y, x)[1] = 0;
noisyImg.at<Vec3b>(y, x)[2] = 0;
}
}
return noisyImg;
}
int testMinAndMax1(){
// 1. 读取单张原图
Mat img = imread(R"(D:\Study\OpenCvStudy\lean.jpg)", ImreadModes::IMREAD_COLOR);
if (img.empty()) {
cout << "图像读取失败!" << endl;
return -1;
}
// 2. 生成带椒盐噪声的变体图
Mat noisyImg = addSaltPepperNoise(img, 0.03); // 3%噪声比例
// 3. min/max 融合
Mat maxFused, minFused,maxminFused;
max(img, noisyImg, maxFused); // 保留亮像素,抑制暗噪声(椒噪声)
min(img, noisyImg, minFused); // 保留暗像素,抑制亮噪声(盐噪声)
// 4. 显示结果
imshow("原图", img);
imshow("带噪声图", noisyImg);
imshow("max融合(抑制暗噪声)", maxFused);
imshow("min融合(抑制亮噪声)", minFused);
waitKey(0);
destroyAllWindows();
return 0;
}

1.4.2 图片亮度调整
cpp
/ 调整图像亮度(delta>0调亮,delta<0调暗)
Mat adjustBrightness(const Mat& src, int delta) {
Mat brightImg;
src.convertTo(brightImg, -1, 1, delta); // 1倍对比度,delta亮度偏移
return brightImg;
}
int testMinAndMax2() {
// 1. 读取单张原图
Mat img = imread(R"(D:\Study\OpenCvStudy\lean.jpg)", ImreadModes::IMREAD_COLOR);
if (img.empty()) {
cout << "图像读取失败!" << endl;
return -1;
}
// 2. 生成亮度变体图
Mat brightImg = adjustBrightness(img, 50); // 调亮50
Mat darkImg = adjustBrightness(img, -50); // 调暗50
// 3. min/max 融合
Mat maxFused, minFused;
max(img, brightImg, maxFused); // 原图+调亮图取最大值,整体更亮
min(img, darkImg, minFused); // 原图+调暗图取最小值,整体更暗
// 4.调亮图+调暗图融合(模拟曝光融合)
Mat exposureFused;
max(brightImg, darkImg, exposureFused);
// 5. 显示结果
imshow("原图", img);
imshow("调亮图", brightImg);
imshow("调暗图", darkImg);
imshow("max融合(整体提亮)", maxFused);
imshow("min融合(整体压暗)", minFused);
imshow("曝光融合(亮部+暗部)", exposureFused);
waitKey(0);
destroyAllWindows();
return 0;
}

1.4.3 亮区提取
cpp
void testMinAndMax3() {
// 1. 读取原图(默认:CV_8UC3,3通道彩色图)
Mat img = imread(R"(D:\Study\OpenCvStudy\lean.jpg)", ImreadModes::IMREAD_COLOR);
if (img.empty()) { // 新增:必加的空值校验,避免后续崩溃
cout << "图像读取失败,请检查路径!" << endl;
return;
}
// 2. 创建阈值图像:匹配原图的【数据类型】和【通道数】
// CV_8UC3:8位无符号字符 + 3通道;Scalar(128,128,128):3通道都设为128
Mat threshold_img(img.size(), CV_8UC3, Scalar(128, 128, 128));
Mat bright_regions, dark_regions;
max(img, threshold_img, bright_regions); // 亮于128保留原值,暗于128设为128
min(img, threshold_img, dark_regions); // 暗于128保留原值,亮于128设为128
imshow("亮区提取", bright_regions);
imshow("暗区提取", dark_regions);
waitKey(0);
destroyAllWindows();
}

二、图像中极值查找
2.1cv::minMaxLoc() 定义
cv::minMaxLoc() 是 OpenCV 中用于在单通道图像中查找最小值、最大值及其对应坐标的专用函数。
cpp
void cv::minMaxLoc(
InputArray src,
double* minVal,
double* maxVal = 0,
Point* minLoc = 0,
Point* maxLoc = 0,
InputArray mask = noArray()
);
| 参数 | 说明 |
|---|---|
src |
输入单通道矩阵(如灰度图、单通道掩膜等)。多通道图像需先分离通道。 |
minVal |
输出型参数,指向最小值的指针。若不需要,可传 NULL。 |
maxVal |
输出型参数,指向最大值的指针。若不需要,可传 NULL。 |
minLoc |
输出型参数,指向最小值位置(Point(x,y))的指针。若不需要,可传 NULL。 |
maxLoc |
输出型参数,指向最大值位置(Point(x,y))的指针。若不需要,可传 NULL。 |
mask |
可选掩膜矩阵,用于限定查找范围。默认 noArray(),表示在全图中查找。 |
2.2 典型用途
- 图像亮度分析:快速定位图像中最亮 / 最暗区域,用于曝光评估或自动曝光控制。
- 目标检测与定位 :在模板匹配(
matchTemplate)或特征检测的响应图中,通过极值点定位目标位置。 - 阈值与分割验证:在阈值处理或图像分割后,验证分割结果的极值,判断是否有效。
- 局部特征分析 :结合
mask参数,在图像的特定区域(如 ROI)内分析极值。
2.3简单应用举例
找寻图片中最亮和最暗的部分
cpp
int testminMaxLoc1() {
// 1. 读取为单通道灰度图
Mat img = imread(R"(D:\Study\OpenCvStudy\lean.jpg)", ImreadModes::IMREAD_GRAYSCALE);
if (img.empty()) {
cout << "图像读取失败!" << endl;
return -1;
}
// 2. 定义变量存储结果
double minVal, maxVal;
Point minLoc, maxLoc;
// 3. 调用 minMaxLoc
minMaxLoc(img, &minVal, &maxVal, &minLoc, &maxLoc);
// 4. 输出结果
cout << "最小值: " << minVal << " @ " << minLoc << endl;
cout << "最大值: " << maxVal << " @ " << maxLoc << endl;
// 可视化:在极值点画圆标记
Mat imgColor;
cvtColor(img, imgColor, COLOR_GRAY2BGR);//格式转换所有通道填充值一样
circle(imgColor, minLoc, 5, Scalar(255, 0, 0), 2); // 蓝色圈标最小值
circle(imgColor, maxLoc, 5, Scalar(0, 0, 255), 2); // 红色圈标最大值
imshow("极值点标记", imgColor);
waitKey(0);
return 0;
}

三、图像像素逻辑运算
3.1像素位运算介绍
像素位运算直接操作图像像素的二进制位(8 位图像对应 0~255 的二进制),是图像处理中掩膜操作、区域提取、图像合成 的基础。OpenCV 中像素位运算函数主要有: bitwise_and/or/not/xor。
| 函数名 | 核心逻辑 | 公式(8 位图像) | 关键要求 | |
|---|---|---|---|---|
bitwise_not |
按位取反(反色) | dst = ~src = 255 - src |
单输入图像 | |
bitwise_and |
按位与(仅双 1 为 1) | dst = src1 & src2 |
src1/src2 尺寸 / 类型完全一致 | |
bitwise_or |
按位或(有 1 则 1) | `dst = src1 | src2` | src1/src2 尺寸 / 类型完全一致 |
bitwise_xor |
按位异或(不同为 1,相同为 0) | dst = src1 ^ src2 |
src1/src2 尺寸 / 类型完全一致 |
函数原型以bitwisw_and为例:
cpp
void bitwise_and(
InputArray src1, // 输入图像1(原图/掩膜)
InputArray src2, // 输入图像2(掩膜/原图)
OutputArray dst, // 输出图像(与输入尺寸/类型一致)
InputArray mask = noArray() // 可选:仅掩膜非零区域执行运算
);
3.2核心函数典型使用场景
3.2.1 bitwise_not(按位取反 / 反色)
- 作用:将每个像素值取反,生成 "底片效果";也用于反转掩膜(黑↔白)。
- 核心场景 :
- 图像反色(如校正颜色反转的摄像头画面);
- 掩膜反转(将 "黑底白目标" 的掩膜转为 "白底黑目标");
- 工业检测中突出暗背景下的亮缺陷。
3.2.2 bitwise_and(按位与)
- 作用 :仅当两个像素对应位都为 1 时结果为 1,核心用于掩膜提取(ROI 抠图)。
- 核心场景 :
- 掩膜抠图(用黑白掩膜保留原图指定区域,屏蔽背景);
- 多条件筛选(同时满足两个阈值的区域保留);
- 图像合成(仅保留两张图的交集区域)。
3.2.3 bitwise_or(按位或)
- 作用 :只要有一个像素对应位为 1 结果为 1,核心用于图像融合 / 区域合并。
- 核心场景 :
- 多区域合并(将多个掩膜的目标区域合并为一个);
- 图像叠加(保留两张图的所有非零区域);
- 缺陷标记(将检测到的多个缺陷区域叠加到原图)。
3.2.4. bitwise_xor(按位异或)
- 作用 :像素值不同则为 1,相同则为 0,核心用于差异检测 / 水印隐藏。
- 核心场景 :
- 图像差异检测(对比两张相似图,高亮不同区域);
- 水印添加 / 提取(异或加密水印,再次异或可还原);
- 动态目标检测(背景建模后,异或提取运动目标)。
3.3 简单应用举例
3.3.1 抠图提取图片的亮区或者暗区
cpp
void testBinaryOperator() {
// 1. 读取图像并预处理
Mat img = imread(R"(D:\Study\OpenCvStudy\lean.jpg)", ImreadModes::IMREAD_COLOR);
if (img.empty()) { cout << "图像读取失败!" << endl; return; }
Mat gray;
cvtColor(img, gray, COLOR_BGR2GRAY); // 转灰度图
// 2. 生成掩膜(提取亮度>150的区域)
Mat mask;
threshold(gray, mask, 150, 255, THRESH_BINARY); // 亮区白,暗区黑
// 3. bitwise_not:反转掩膜
Mat mask_not;
bitwise_not(mask, mask_not); // 亮区黑,暗区白
// 4. bitwise_and:掩膜抠图
Mat result_bright, result_dark;
// 提取亮区:mask为白的区域保留原图,黑的区域置0
bitwise_and(img, img, result_bright, mask);//自身image与操作图像不变 在与mask掩膜
// 提取暗区:用反转后的掩膜提取暗部
bitwise_and(img, img, result_dark, mask_not);
// 5. 显示结果
imshow("原图", img);
imshow("掩膜(亮区白)", mask);
imshow("反转掩膜(暗区白)", mask_not);
imshow("提取亮区", result_bright);
imshow("提取暗区", result_dark);
waitKey(0);
destroyAllWindows();
}

3.3.2 图片简单添加水印
cpp
int testBinaryOperator2() {
// 读取原图和水印图
Mat img = imread(R"(D:\Study\OpenCvStudy\lean.jpg)", ImreadModes::IMREAD_COLOR); // 背景图
Mat logo = imread(R"(D:\Study\OpenCvStudy\White.png)", IMREAD_UNCHANGED);
if (img.empty() || logo.empty()) return -1;
// 分离水印的BGR通道和Alpha通道(透明度)
std::vector<Mat> layers;
split(logo, layers);
// 提取BGR三通道,合并为彩色水印图
vector<Mat> bgr_layers = { layers[0], layers[1], layers[2] };
Mat logo_bgr;
merge(bgr_layers, logo_bgr);
Mat alpha = layers[3]; // Alpha通道(0=透明,255=不透明)
// 定义水印在原图中的位置(左上角)
Rect roi(50, 50, logo.cols, logo.rows);
Mat img_roi = img(roi); // 原图中对应区域
// 生成掩膜:用于挖空原图
Mat mask;
bitwise_not(alpha, mask); // 透明区域变白,不透明区域变黑
//从原图中"挖去"水印位置
Mat img_bg;
bitwise_and(img_roi, img_roi, img_bg, mask);
// 从水印中"抠出"水印内容
Mat logo_fg;
bitwise_and(logo_bgr, logo_bgr, logo_fg, alpha);
// 合并:挖去的背景 + 抠出的水印
Mat blended;
//add(img_bg, logo_fg, blended);完全堆叠无透明效果
double alpha_weight = 0.3; // 水印透明度:0.0(全透)~ 1.0(不透明)
addWeighted(img_roi, 1 - alpha_weight, logo_fg, alpha_weight, 0, blended);
//将结果放回原图
blended.copyTo(img_roi);
imshow("带水印效果", img);
waitKey(0);
return 0;
}
