准备工作:
首先,配置安装opencv可以戳这里:VS2017配置opencv教程(超详细!!!)
追求可视化的话可以下载一个插件 Image Watch(下载戳这里!) 选择合适的版本进行下载,双击即可完成安装。调试时如果没有显示Image Watch窗口,点击菜单栏 --> 视图 --> 其他窗口 --> image watch,调出该插件。
其次,开头废话多唠一点,因为 100 个有点多,想摆放的整齐一点,所以这些问题我都会放在自己的命名空间 mytinycv 下,引用的其他文件都在头文件中注明,指定的命名空间在源文件中注明,对函数的测试放在类 ImageProcessTest 中用 void 函数封装一下,在 main 直接调用即可,这里我就只贴函数 + 调用 + 显示的结果了。
项目:
项目地址:ImageProcessing100Wen(极客教程地址)
收集了为图像处理初学者设计的 100 个问题以及对应的理论知识。和蝾螈一起学习基本的图像处理知识,理解图像处理算法吧!
解答这里提出的问题请不要调用OpenCV的API,自己动手实践。相信对你掌握opencv有比较好的帮助。
问题 1-8: | |||||
---|---|---|---|---|---|
1 | 通道交换 | ||||
2 | 灰度化(Grayscale) | ||||
3 | 二值化(Thresholding) | ||||
4 | 大津二值化算法(Otsu's Method) | ||||
5 | HSV 变换 | ||||
6 | 减色处理 | ||||
7 | 平均池化(Average Pooling) | ||||
8 | 最大池化(Max Pooling) |
Q1: 通道交换
问题说明:
RGB代表红绿蓝。大多数情况下,RGB颜色存储在结构或无符号整数中。BGR除了区域顺序颠倒其他是相同的。
早期开发者使用BGR作为颜色的空间是因为:那个时候的BGR格式在相机制造厂商和软件提供商之间比较受欢迎。感兴趣可以戳:为什么使用BGR格式?
在opencv中,我们来看下如何读取图像,然后将 RGB 通道替换成 BGR 通道这样的Opencv通道交换。
下面的代码用于提取图像的红色通道。注意,cv2.imread() 的系数是按 BGR 顺序排列的!其中的变量 red 表示的是仅有原图像红通道的 imori.jpg。
C++实现:
Mat SwapChannels(Mat srcImg){
int width = srcImg.cols;
int height = srcImg.rows;
Mat dstImg = Mat::zeros(height, width, CV_8UC3);
//交换通道顺序
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
dstImg.at<Vec3b>(y, x)[0] = srcImg.at<Vec3b>(y, x)[2];
dstImg.at<Vec3b>(y, x)[2] = srcImg.at<Vec3b>(y, x)[0];
dstImg.at<Vec3b>(y, x)[1] = srcImg.at<Vec3b>(y, x)[1];
}
}
return dstImg;
}
测试及结果:
int main(){
Mat image = imread("..\\Resource\\imori.jpg");
Mat bgr = SwapChannels(image);
namedWindow("sample", 0);
imshow("sample", bgr);
waitKey(0);
destroyAllWindows();
return 0;
}
Q2: 灰度化(Grayscale)
问题说明:
灰度是一种图像亮度的表示方法,将彩色图像转化成为灰度图像的过程称为图像的灰度化处理。
在RGB模型中,如果 R = G = B R=G=B R=G=B 时,则彩色表示一种灰度颜色,其中 R = G = B R=G=B R=G=B 的值叫灰度值,因此,灰度图像每个像素只需一个字节存放灰度值(又称强度值、亮度值),灰度范围为 0-255。
图像灰度化的算法主要有以下 3 种:
-
最大值法
将彩色图像中的三分量亮度的最大值作为灰度图的灰度值。即 R = G = B = m a x ( R , G , B ) R=G=B=max(R,G,B) R=G=B=max(R,G,B)
-
平均值法
将彩色图像中的三分量亮度求平均得到一个灰度值。即 R = G = B = ( R + G + B ) / 3 R=G=B=(R+G+B) / 3 R=G=B=(R+G+B)/3
-
加权平均法
根据重要性及其它指标,将三个分量以不同的权值进行加权平均。由于人眼对绿色的敏感最高,对蓝色敏感最低,因此 w w wG> w w wR> w w wB 时能得到较合理的灰度图像),按下式对RGB三分量进行加权平均。即 R = G = B = ( w R R + w G G + w B B ) / 3 R=G=B=(w_RR+w_GG+w_BB) / 3 R=G=B=(wRR+wGG+wBB)/3
OpenCV开放库所采用的灰度权值:
G r a y = 0.072169 B + 0.715160 G + 0.212671 R Gray= 0.072169B+0.715160G+0.212671R Gray=0.072169B+0.715160G+0.212671R
从人体生理学角度所提出的一种权值:
G r a y = 0.11 B + 0.59 G + 0.3 R Gray= 0.11B+0.59G+0.3R Gray=0.11B+0.59G+0.3R
这里只用 G r a y = 0.072169 B + 0.715160 G + 0.212671 R Gray= 0.072169B+0.715160G+0.212671R Gray=0.072169B+0.715160G+0.212671R 来实现。
C++实现:
Mat BGR2GRAY(Mat srcImg) {
int width = srcImg.cols;
int height = srcImg.rows;
Mat dstImg = Mat::zeros(height, width, CV_8UC1);
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
dstImg.at<uchar>(y, x) = 0.072169*(float)srcImg.at<Vec3b>(y, x)[0]\
+ 0.715160*(float)srcImg.at<Vec3b>(y, x)[1]\
+ 0.212671*(float)srcImg.at<Vec3b>(y, x)[2];
}
}
return dstImg;
}
测试及结果:
int main(){
Mat image = imread("..\\Resource\\imori.jpg");
Mat gray = BGR2GRAY(image);
namedWindow("sample", 0);
imshow("sample", gray);
waitKey(0);
destroyAllWindows();
return 0;
}
Q3: 二值化(Thresholding)
问题说明:
图像二值化是将 256 个亮度等级的灰度图像通过适当的阈值选取,仍然可以获得反映图像整体和局部特征的方法,它使图像变得简单,减小数据量,也能凸显出感兴趣的目标的轮廓。
所有灰度大于或等于阈值的像素被判定为属于特定物体,其灰度值用 255 表示,否则这些像素点被排除在物体区域以外;灰度值为 0 表示背景或者例外的物体区域。
这里我们将灰度的阈值设置为 128 来进行二值化:
即 y = 0 (if y < 128)
255 (else)
C++实现:
Mat Binarize(Mat srcGray, int th) {
int width = srcGray.cols;
int height = srcGray.rows;
Mat dstImg = Mat::zeros(height, width, CV_8UC1);
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
if (srcGray.at<uchar>(y, x) < th)
dstImg.at<uchar>(y, x) = 0;
else
dstImg.at<uchar>(y, x) = 255;
}
}
return dstImg;
}
测试及结果:
int main(){
Mat image = imread("..\\Resource\\imori.jpg");
Mat gray = BGR2GRAY(image);
Mat binary = Binarize(gray, 128);
namedWindow("sample", 0);
imshow("sample", binary);
waitKey(0);
destroyAllWindows();
return 0;
}
Q4: 大津二值化算法(Otsu’s Method)
问题说明:
大津二值化算法也被称作最大类间方差法,是一种可以自动确定二值化中阈值的算法,从类内方差和类间方差的比值计算得来:
- 小于阈值 t t t 的类记作 0,大于阈值 t t t 的类记作 1;
- w 0 w_0 w0 和 w 1 w_1 w1 是被阈值 t t t 分开的两个类中的像素数占总像素数的比率(满足 w 0 + w 1 = 1 w_0 + w_1 =1 w0+w1=1);
- S 0 2 S_0^2 S02, S 1 2 S_1^2 S12 是这两个类中像素值的方差;
- M 0 M_0 M0, M 1 M_1 M1 是这两个类的像素值的平均值;
也就是说:
类内方差: S w 2 S_w^2 Sw2 = w 0 w_0 w0 * S 0 2 S_0^2 S02 + w 1 w_1 w1 * S 1 2 S_1^2 S12
类间方差: S b 2 S_b^2 Sb2 = w 0 w_0 w0 * ( M 0 M_0 M0 - M t M_t Mt)2 + w 1 w_1 w1 * ( M 1 M_1 M1 - M t M_t Mt)2 = w 0 w_0 w0 * w 1 w_1 w1 * ( M 0 M_0 M0 - M 1 M_1 M1)2
图像所有像素的方差: S t 2 S_t^2 St2 = S w 2 S_w^2 Sw2 + S b 2 S_b^2 Sb2 = (const)
根据以上的式子,分离度: X X X = S b 2 S w 2 S_b^2 \over S_w^2 Sw2Sb2 = S b 2 S t 2 − S w 2 S_b^2 \over {S_t^2 - S_w^2} St2−Sw2Sb2,函数图像大致如下 (只取不小于 0 的部分):
即
a
r
g
m
a
x
t
X
argmax_tX
argmaxtX =
a
r
g
m
a
x
t
S
b
2
argmax_tS_b^2
argmaxtSb2,如果
S
b
2
S_b^2
Sb2 最大,就可以得到最好的二值化阈值 t。
C++实现:
Mat OtsuBinarize(Mat srcGray) {
int width = srcGray.cols;
int height = srcGray.rows;
double w0 = 0, w1 = 0;
double m0 = 0, m1 = 0;
double maxVariance = 0, variance = 0;
int th = 0;
//遍历枚举求类间方差最大时的阈值th
for (int tmpth = 0; tmpth < 255; tmpth++) {
w0 = w1 = 0;
m0 = m1 = 0;
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
if (srcGray.at<uchar>(y, x) < tmpth) {
++w0;
m0 += srcGray.at<uchar>(y, x);
}
else {
++w1;
m1 += srcGray.at<uchar>(y, x);
}
}
}
//计算每类的像素平均值和每类占比
m0 = (w0 == 0) ? 0: m0 / w0;
m1 = (w1 == 0) ? 0 : m1 / w1;
w0 = w0 / (width*height);
w1 = w1 / (width*height);
variance = w0 * w1*pow(m0 - m1, 2);
if (variance > maxVariance) {
maxVariance = variance;
th = tmpth;
}
}
//再利用th进行二值化操作
Mat dstImg = Binarize(srcGray, th);
return dstImg;
}
测试及结果:
int main(){
Mat image = imread("..\\Resource\\imori.jpg");
Mat gray = BGR2GRAY(image);
Mat ostu = OtsuBinarize(gray);
namedWindow("sample", 0);
imshow("sample", ostu);
waitKey(0);
destroyAllWindows();
return 0;
}
Q5: HSV 变换
问题说明:
HSV 变换就是 HSV 色彩和 RGB 色彩之间互相转换的过程。
HSV 是使用色相(Hue)、饱和度(Saturation)、明度(Value)来表示色彩的一种方式。
- 色相:将颜色使用 0 到 360 度表示,就是平常所说的颜色名称,如红色、蓝色。色相与数值按下表对应:
红 | 黄 | 绿 | 青 | 蓝 | 品红 | 红 |
---|---|---|---|---|---|---|
0 | 60 | 120 | 180 | 240 | 300 | 360 |
- 饱和度:是指色彩的纯度,饱和度越低则颜色越黯淡( 0 ≤ S < 1 0\leq S < 1 0≤S<1);
- 明度:即颜色的明暗程度。数值越高越接近白色,数值越低越接近黑色 ( 0 ≤ V < 1 0 \leq V < 1 0≤V<1);
从 RGB 色彩表示转换到 HSV 色彩表示通过以下方式计算:
R,G,B的值在 [0, 1] 之间:
M a x = m a x ( R , G , B ) Max = max(R,G,B) Max=max(R,G,B)
M i n = m i n ( R , G , B ) Min = min(R,G,B) Min=min(R,G,B)
H = 0 , ( i f M i n = M a x ) H =\ 0, \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (if \ \ \ Min=Max) H= 0, (if Min=Max)
60 ∗ ( G − R ) / ( M a x − M i n ) + 60 , ( i f M i n = B ) \ \ \ \ \ \ \ \ \ \ 60 * (G-R) / (Max-Min) + 60,\ \ \ \ \ \ \ \ \ \ \ \ (if \ \ \ Min=B) 60∗(G−R)/(Max−Min)+60, (if Min=B)
60 ∗ ( B − G ) / ( M a x − M i n ) + 180 , ( i f M i n = R ) \ \ \ \ \ \ \ \ \ \ 60 * (B-G) / (Max-Min)+180, \ \ \ \ \ \ \ \ \ \ (if \ \ \ Min=R) 60∗(B−G)/(Max−Min)+180, (if Min=R)
60 ∗ ( R − B ) / ( M a x − M i n ) + 300 , ( i f M i n = G ) \ \ \ \ \ \ \ \ \ \ 60 * (R-B) / (Max-Min)+300 ,\ \ \ \ \ \ \ \ \ \ (if \ \ \ Min=G) 60∗(R−B)/(Max−Min)+300, (if Min=G)
V = M a x V = Max V=Max
S = M a x − M i n S = Max - Min S=Max−Min
HSV -> RGB 转换由以下公式定义:
C = S C = S C=S
H ′ = H / 60 H' = H / 60 H′=H/60
X = C ∗ ( 1 − ∣ H ′ m o d 2 − 1 ∣ ) X = C * (1 - |H' mod\ 2 - 1|) X=C∗(1−∣H′mod 2−1∣)
( R , G , B ) = ( V − C ) ( 1 , 1 , 1 ) + ( 0 , 0 , 0 ) ( i f H i s u n d e f i n e d ) (R,G,B) = (V - C) (1,1,1) + (0, 0, 0)\ \ \ \ \ \ \ \ \ (if \ \ H\ \ is \ \ undefined) (R,G,B)=(V−C)(1,1,1)+(0,0,0) (if H is undefined)
( C , X , 0 ) ( i f 0 ≤ H ′ < 1 ) \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (C, X, 0)\ \ \ \ \ \ \ (if\ \ 0 \leq H' < 1) (C,X,0) (if 0≤H′<1)
( X , C , 0 ) ( i f 1 ≤ H ′ < 2 ) \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (X, C, 0) \ \ \ \ \ \ \ (if \ \ 1 \leq H' < 2) (X,C,0) (if 1≤H′<2)
( 0 , C , X ) ( i f 2 ≤ H ′ < 3 ) \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (0, C, X) \ \ \ \ \ \ \ (if \ \ 2 \leq H' < 3) (0,C,X) (if 2≤H′<3)
( 0 , X , C ) ( i f 3 ≤ H ′ < 4 ) \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (0, X, C)\ \ \ \ \ \ \ (if \ \ 3 \leq H' < 4) (0,X,C) (if 3≤H′<4)
( X , 0 , C ) ( i f 4 ≤ H ′ < 5 ) \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (X, 0, C) \ \ \ \ \ \ \ (if \ \ 4\leq H' < 5) (X,0,C) (if 4≤H′<5)
( C , 0 , X ) ( i f 5 ≤ H ′ < 6 ) \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (C, 0, X)\ \ \ \ \ \ \ (if \ \ 5 \leq H' < 6) (C,0,X) (if 5≤H′<6)
请将色相反转(色相值加180),然后再用 RGB 色彩空间表示图片。
C++实现:
//BGR->HSV
Mat BGR2HSV(Mat srcImg) {
int width = srcImg.cols;
int height = srcImg.rows;
float b, g, r;
float h, s, v;
float _max, _min;
Mat dstImg = Mat::zeros(height, width, CV_32FC3);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
b = (float)srcImg.at<Vec3b>(y, x)[0] / 255;
g = (float)srcImg.at<Vec3b>(y, x)[1] / 255;
r = (float)srcImg.at<Vec3b>(y, x)[2] / 255;
_max = fmax(b, fmax(g, r));
_min = fmin(b, fmin(g, r));
//计算色相Hue 将颜色使用0-360度表示
if (_max == _min)
h = 0;
else if (_min == b)
h = 60 * (g - r) / (_max - _min) + 60;
else if (_min == r)
h = 60 * (b - g) / (_max - _min) + 180;
else if (_min == g)
h = 60 * (r - b) / (_max - _min) + 300;
//计算饱和度Saturation
s = _max - _min;
//计算明度Value
v = _max;
dstImg.at<Vec3f>(y, x)[0] = h;
dstImg.at<Vec3f>(y, x)[1] = s;
dstImg.at<Vec3f>(y, x)[2] = v;
}
}
return dstImg;
}
//色相反转
Mat InverseHue(Mat srcHsv) {
int width = srcHsv.cols;
int height = srcHsv.rows;
Mat dstImg = srcHsv.clone();
//对每个点色相值+180
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
dstImg.at<Vec3f>(y, x)[0] = fmod(srcHsv.at<Vec3f>(y, x)[0] + 180, 360);
}
}
return dstImg;
}
//HSV->BGR
Mat HSV2BGR(Mat srcHsv) {
int width = srcHsv.cols;
int height = srcHsv.rows;
float h, s, v;
float c, _h, _x;
float r, g, b;
Mat dstImg = Mat::zeros(height, width, CV_8UC3);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
h = srcHsv.at<Vec3f>(y, x)[0];
s = srcHsv.at<Vec3f>(y, x)[1];
v = srcHsv.at<Vec3f>(y, x)[2];
c = s;
_h = h / 60;
_x = c * (1 - abs(fmod(_h, 2) - 1));
r = g = b = v - c;
//v-c 即_max-(_max-_min)>=0
if (_h >= 0 && _h < 1) {
r += c;
g += _x;
}
else if (_h < 2) {
r += _x;
g += c;
}
else if (_h < 3) {
g += c;
b += _x;
}
else if (_h < 4) {
g += _x;
b += c;
}
else if (_h < 5) {
r += _x;
b += c;
}
else if (_h < 6) {
r += c;
b += _x;
}
dstImg.at<Vec3b>(y, x)[0] = (uchar)(b * 255);
dstImg.at<Vec3b>(y, x)[1] = (uchar)(g * 255);
dstImg.at<Vec3b>(y, x)[2] = (uchar)(r * 255);
}
}
return dstImg;
}
测试及结果:
int main(){
Mat image = imread("..\\Resource\\imori.jpg");
Mat hsv = BGR2HSV(image);
namedWindow("sample1", 0);
imshow("sample1", hsv);
hsv = InverseHue(hsv);
namedWindow("sample2", 0);
imshow("sample2", hsv);
Mat bgr = HSV2BGR(hsv);
namedWindow("sample3", 0);
imshow("sample3", bgr);
waitKey(0);
destroyAllWindows();
return 0;
}
Q6: 减色处理
问题说明:
我们将图像的值由 2563 压缩至 43,即 RGB 的值只取 32,96,160,224,这被称作色彩量化。色彩的值按照下面的方式定义:
v a l = 32 ( 0 ≤ v a l < 64 ) val = 32 \ \ \ \ \ \ \ ( 0 \leq val < 64) val=32 (0≤val<64)
96 ( 64 ≤ v a l < 128 ) \ \ \ \ \ \ \ \ \ \ \ 96 \ \ \ \ \ \ \ ( 64 \leq val < 128) 96 (64≤val<128)
160 ( 128 ≤ v a l < 192 ) \ \ \ \ \ \ \ \ \ \ \ 160 \ \ \ \ \ (128 \leq val < 192) 160 (128≤val<192)
224 ( 192 ≤ v a l < 256 ) \ \ \ \ \ \ \ \ \ \ \ 224 \ \ \ \ \ (192 \leq val < 256) 224 (192≤val<256)
C++实现:
Mat DecreaseColor(Mat srcImg) {
int width = srcImg.cols;
int height = srcImg.rows;
int channel = srcImg.channels();
Mat dstImg = Mat::zeros(height, width, CV_8UC3);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
for (int c = 0; c < channel; c++) {
dstImg.at<Vec3b>(y, x)[c] = (uchar)(floor((double)srcImg.at<Vec3b>(y, x)[c] / 64) * 64 + 32);
}
}
}
return dstImg;
}
测试及结果:
int main(){
Mat image = imread("..\\Resource\\imori.jpg");
Mat img = DecreaseColor(image);
namedWindow("sample", 0);
imshow("sample", img);
waitKey(0);
destroyAllWindows();
return 0;
}
Q7: 平均池化(Average Pooling)
问题说明:
将图片按照固定大小网格分割,网格内的像素值取网格内所有像素的平均值。我们将这种把图片使用均等大小网格分割,并求网格内代表值的操作称为池化(Pooling)。池化操作是卷积神经网络(Convolutional Neural Network)中重要的图像处理方式。平均池化按照下式定义:
v = 1 ∣ R ∣ ∗ ∑ i R v i v = \frac{1}{|R|} * \sum_i^R v_i v=∣R∣1∗∑iRvi
请把大小为 128×128 的 imori.jpg 使用 8×8 的网格做平均池化。
C++实现:
Mat AveragePooling(Mat srcImg) {
int width = srcImg.cols;
int height = srcImg.rows;
int channel = srcImg.channels();
Mat dstImg = Mat::zeros(height, width, CV_8UC3);
//网格尺寸8*8
int kernelSize = 8;
double val = 0;
//边界处理上,最好保证图片能被网格均分,这里简单的舍弃了不能被均分的部分
for (int y = 0; y < height - height % kernelSize; y += kernelSize) {
for (int x = 0; x < width - width % kernelSize; x += kernelSize) {
for (int c = 0; c < channel; c++) {
//分别对网格内值求和求均值存入输出图中
val = 0;
for (int dy = 0; dy < kernelSize; dy++) {
for (int dx = 0; dx < kernelSize; dx++) {
val += (double)srcImg.at<Vec3b>(y + dy, x + dx)[c];
}
}
val /= (kernelSize * kernelSize);
for (int dy = 0; dy < kernelSize; dy++) {
for (int dx = 0; dx < kernelSize; dx++) {
dstImg.at<Vec3b>(y + dy, x + dx)[c] = (uchar)val;
}
}
}
}
}
return dstImg;
}
测试及结果:
int main(){
Mat image = imread("..\\Resource\\imori.jpg");
Mat img = AveragePooling(image);
namedWindow("sample", 0);
imshow("sample", img);
waitKey(0);
destroyAllWindows();
return 0;
}
Q8: 最大池化(Max Pooling)
问题说明:
网格内的值不取平均值,而是取网格内的最大值进行池化操作,叫做最大池化。
请把大小为 128×128 的 imori.jpg 使用 8×8 的网格做最大池化。
C++实现:
Mat MaximumPooling(Mat srcImg) {
int width = srcImg.cols;
int height = srcImg.rows;
int channel = srcImg.channels();
Mat dstImg = Mat::zeros(height, width, CV_8UC3);
//网格尺寸8*8
int kernelSize = 8;
double val = 0;
//边界处理上,最好保证图片能被网格均分,这里简单的舍弃了不能被均分的部分
for (int y = 0; y < height - height % kernelSize; y += kernelSize) {
for (int x = 0; x < width - width % kernelSize; x += kernelSize) {
for (int c = 0; c < channel; c++) {
//求取网格内最大值
val = 0;
for (int dy = 0; dy < kernelSize; dy++) {
for (int dx = 0; dx < kernelSize; dx++) {
val = fmax((double)srcImg.at<Vec3b>(y + dy, x + dx)[c], val);
}
}
for (int dy = 0; dy < kernelSize; dy++) {
for (int dx = 0; dx < kernelSize; dx++) {
dstImg.at<Vec3b>(y + dy, x + dx)[c] = (uchar)val;
}
}
}
}
}
return dstImg;
}
测试及结果:
int main(){
Mat image = imread("..\\Resource\\imori.jpg");
Mat img = MaximumPooling(image);
namedWindow("sample", 0);
imshow("sample", img);
waitKey(0);
destroyAllWindows();
return 0;
}