图像处理降采样列怎么变化

2013-06-02 20:37:03 zhubenfulovepoem 阅读数 6900
  • 信息隐蔽

    1.使用容器平台解决了当前哪些实际的问题2.为后来者做容器选型提供参考建议3.通过技术复盘,整理一些具体问题的解决方案,少走弯路

    11人学习 任铄
    免费试看
#include <cv.h>
#include <highgui.h>
#include <cxcore.h>
#include <stdio.h>
#include <iostream> 
#include <iomanip>  
#include <string>


#define Im(ROW,COL) ((float *)(im->data.fl + im->step/sizeof(float) *(ROW)))[(COL)]  
#define Imnew(ROW,COL) ((float *)(imnew->data.fl + imnew->step/sizeof(float) *(ROW)))[(COL)]


//下采样原来的图像,返回缩小2倍尺寸的图像  
CvMat * halfSizeImage(CvMat * im)   
{
unsigned int i,j;  
int w = im->cols/2;  
int h = im->rows/2;   
CvMat *imnew = cvCreateMat(h, w, CV_8UC1);


//由于IplImage是4通道的step,所有最好保持im输入图像和imnew的step一致。
imnew->step = im->step/2;
printf("\n1、halfsize后的步长:%d",imnew->step);printf("halfsize前的步长:%d\n",im->step);






printf("2、halfsize后的步长:%d",imnew->step);printf("halfsize前的步长:%d\n",im->step);
for ( j = 0; j < h; j++)   
for ( i = 0; i < w; i++)   
Imnew(j,i)=Im(j*2, i*2);  
return imnew;  
}


int main( int argc, char* argv[] ) {
IplImage* src = NULL;
IplImage* gray_src = NULL;
CvMat* src_mat;


IplImage* halfsize_src = NULL;
CvMat* halfsize_src_mat;


//读取图片
if((src=cvLoadImage("1.bmp",1)) ==0 )
return -1;
cvNamedWindow("source image",1);
cvNamedWindow("halfSizeImage",1);
cvNamedWindow("src_mat_src",1);
cvShowImage("source image",src);


//转化成单通道图像再处理
gray_src = cvCreateImage(cvSize(src->width,src->height),IPL_DEPTH_8U,1);
cvCvtColor(src,gray_src,CV_BGR2GRAY);
cvNamedWindow("source gray image",1);
cvShowImage("source gray image",gray_src);

//为图像阵列分配内存,假设两幅图像的大小相同,tempMat跟随image1的大小
//
src_mat = cvCreateMat(src->height,src->width,CV_8UC1 );
//


//转换进入Mat数据结构,图像操作使用的是浮点型操作
cvConvert(gray_src,src_mat);
src_mat->step = gray_src->widthStep;
printf("src_mat size:[%d %d]\n",src_mat->cols,src_mat->rows);


//将图像数据还原成图像8位无符号单通道
IplImage* src_mat_src = cvCreateImage(cvGetSize(src),8,1);




    cvGetImage(src_mat,src_mat_src);
src_mat_src->widthStep = src->width;//显示时候更改好
printf("src_mat_src %d\n",src_mat_src->widthStep);




cvShowImage("src_mat_src",src_mat_src);
printf("%d %d\n",src_mat->step,gray_src->widthStep);




halfsize_src = cvCreateImage(cvSize(src->width/2,src->height/2),8,1); 


halfsize_src_mat = cvCreateMat(src->height/2,src->width/2,CV_8UC1 );
halfsize_src_mat->step = gray_src->widthStep/2;
printf("%d %d",halfsize_src_mat->step,gray_src->widthStep);


halfsize_src_mat = halfSizeImage(src_mat);
printf("size:[%d %d]\n",halfsize_src_mat->width,halfsize_src_mat->height);


// halfsize_src_mat->step=halfsize_src->widthStep;


printf("size:[%d %d]\n",halfsize_src->width,halfsize_src->height);


    cvGetImage(halfsize_src_mat,halfsize_src);
halfsize_src->widthStep = gray_src->widthStep/2;
printf("%d \n",halfsize_src->widthStep);




cvShowImage("halfSizeImage",halfsize_src);
printf("%d \n",halfsize_src->widthStep);
// cv::namedWindow("halfSizeImage", 1);
// cv::imshow("halfSizeImage",temp1);




cvWaitKey(0);
cvDestroyWindow("source image");
cvDestroyWindow("halfSizeImage");
cvReleaseImage( &src );
// cvReleaseImage( &src );




}
2015-07-29 21:23:47 Kena_M 阅读数 5786
  • 信息隐蔽

    1.使用容器平台解决了当前哪些实际的问题2.为后来者做容器选型提供参考建议3.通过技术复盘,整理一些具体问题的解决方案,少走弯路

    11人学习 任铄
    免费试看

降采样函数的高效实现

所谓降采样,就是将图像的尺寸缩减为原来的一半,隔行隔列取值,降采样在构建图像金字塔时是需要反复使用的。
假如原图像尺寸为W×H,那么降采样之后尺寸变为W2×H2,长度和宽度是奇数还是偶数,都会自动向下取整。
另外之所以发这么一个小儿科功能的文章是因为本人患有重度强迫症,这么简单的功能OpenCV肯定有,但我就是不想用,我不知道它是如何实现的,对速度不放心。由于强迫症,我对于图像数据的访问,坚持以下几个原则:

  • 能用一维向量就坚决不用二维的,因为连续存储的数据本来就是按照一维线性存储的,所谓二维是人类具象化的产物
  • 能用指针就坚决不要难过数组下标,因为根据标准C,访问指针要比访问数组更高效,访问数组时涉及到下标的索引计算,这对我来说是绝不能容忍的
  • 在使用指针一直出错烧脑时才用回数组

由此,编写了如下的函数,能看懂就尽量看吧,反正不好看,看不懂也直接可以拿来用,目前只处理了8位灰度图,太小儿科,在此露相纯粹是展示一下强迫症的魅力,就是为了追求处理的快速性,不惜脑细胞的码这种代码,反正我就认为这样的代码运行起来快速高效

unsigned char* downSampling(unsigned char *src, int width, int height, int *dwidth, int *dheight)
{
    int oddw = width % 2;
    int oddh = height % 2;
    int flag;
    *dwidth = width / 2;
    *dheight = height / 2;
    unsigned char *dst = Malloc(unsigned char, (*dwidth) * (*dheight));
    if(oddw + oddh == 0)flag = 0;
    else if(oddw + oddh == 2) flag = 3;
    else if(oddw == 0 && oddh == 1) flag = 1;
    else flag = 2;
    int i;
    unsigned char *pCur, *pEnd, *pdCur, *pdEnd;
    switch(flag)
    {
        case 0:
            for(i = 0, pCur = src, pEnd = src + width * height, pdCur = dst, pdEnd = dst + (*dwidth) * (*dheight); pdCur < pdEnd; pCur += 2, pdCur++)
            {
                i++;
                *pdCur=*pCur;
                if(i==*dwidth)
                {
                    pCur+=width;
                    i=0;
                }

            }
            break;
        case 1:
            for(i = 0, pCur = src, pEnd = src + width * height, pdCur = dst, pdEnd = dst + (*dwidth) * (*dheight); pdCur < pdEnd; pCur += 2, pdCur++)
            {
                i++;
                *pdCur=*pCur;
                if(i==*dwidth)
                {
                    pCur+=width;
                    i=0;
                }
            }
            break;
        case 2:
            for(i = 0, pCur = src, pEnd = src + width * height, pdCur = dst, pdEnd = dst + (*dwidth) * (*dheight); pdCur < pdEnd; pCur += 2, pdCur++)
            {
                i++;
                *pdCur=*pCur;
                if(i==*dwidth)
                {
                    pCur+=width+1;
                    i=0;
                }
            }
            break;
        case 3:
            for(i = 0, pCur = src, pEnd = src + width * height, pdCur = dst, pdEnd = dst + (*dwidth) * (*dheight); pdCur < pdEnd; pCur += 2, pdCur++)
            {
                i++;
                *pdCur=*pCur;
                if(i==*dwidth)
                {
                    pCur+=width+1;
                    i=0;
                }
            }
            break;
    }
    return dst;
}

FAQ:

  1. 为什么分了4个case ? 因为涉及到长度和宽度不一定为偶数啊,要是奇数时到行末地址要跳格的,自己好好想想;
  2. 为什么四种情况的前两种和后两种完全一样 ? 这是强迫症病人为以后可能出现的其他种情况留作升级用的备胎;
  3. 为什么用C语言?我只会C语言

效果

这里写图片描述


2018-08-15 15:41:20 qq_26907755 阅读数 1643
  • 信息隐蔽

    1.使用容器平台解决了当前哪些实际的问题2.为后来者做容器选型提供参考建议3.通过技术复盘,整理一些具体问题的解决方案,少走弯路

    11人学习 任铄
    免费试看

图像金字塔:最底下图像尺寸最大,最上面最小。
上采样:当前图像分辨率从图像金字塔的低分辨率到高分辨率采样,得到的是一个更高分辨率的图像。
降采样:当前图像分辨率从图像金字塔的高分辨率到低分辨率采样,得到的是一个更低分辨率的图像。
高斯金字塔:
从低向上,逐层降采样得到
降采样之后图像大小是原图像MXN的M/2XN/2,就是对原图像删除偶数行与列,
即得到降采样之后上一层的图片
高斯金字塔的生成过程分为两步:

  1. 对当前层进行高斯模糊
  2. 删除当前层的偶数行列

即得到上一层的图像,这样上一层和下一层相比,都只有它的1/4大小

高斯不同:(DOG)
定义:就是把同一张图像在不同的参数下做高斯模糊之后的结果相减,得到的输出图像称为高斯不同。
高斯不同是图像的内在特征,在灰度图像增强,角点检测中经常用到。

拉普拉斯金字塔:
对图像的向上取样
将图像在每个方向扩大为原来的两倍,新增的行和列以0填充
使用先前同样的内核(乘以4)与放大后的图像卷积,获得 “新增像素”的近似值
得到的图像即为放大后的图像,但是与原来的图像相比会发觉比较模糊,因为在缩放的过程中已经丢失了一些信息,如果想在缩小和放大整个过程中减少信息的丢失,这些数据形成了拉普拉斯金字塔。

pyrUp()函数剖析:

void pyrUp(InputArray src,//输入图像,即源图像,填Mat类的对象即可
           OutputArraydst,//输出图像,和源图片有一样的尺寸和类型。
           const Size& dstsize=Size(),// 输出图像的大小;有默认值Size(),即默认情况下,由Size(src.cols*2,src.rows*2)来进行计算
           int borderType=BORDER_DEFAULT )//边界模式,一般我们不用去管它

pyrDown()函数剖析:

void pyrDown(InputArray src,
             OutputArray dst, 
             const Size& dstsize=Size(), 
             int borderType=BORDER_DEFAULT)

代码:

#include <opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
#include <string> 
#include<fstream> 
using namespace cv;
using namespace std;

int main() {
    Mat src, dst_up, dst_down;
    src = imread("C:\\Users\\Administrator\\Desktop\\pic\\5.jpg");
    imshow("input", src);
    pyrUp(src, dst_up, Size(src.cols * 2, src.rows * 2));
    imshow("pyrUp", dst_up);
    pyrDown(src, dst_down, Size(src.cols / 2, src.rows / 2));
    imshow("pyrDown", dst_down);
    //高斯不同:(DOG)
    Mat gray_src, g1, g2, dog;
    cvtColor(src, gray_src, CV_RGB2GRAY);
    GaussianBlur(gray_src, g1, Size(3, 3),0,0);
    GaussianBlur(g1, g2, Size(3, 3), 0, 0);
    subtract(g1, g2, dog);
    imshow("DOG", dog);
    waitKey(0);
}

结果:
这里写图片描述

resize( )函数剖析:
resize( )为OpenCV中专职调整图像大小的函数。

void resize(InputArray src,//输入图像,即源图像,填Mat类的对象即可。
            OutputArray dst,//输出图像,当其非零时,dsize(第三个参数)的尺寸,或者由src.size()计算出来
            Size dsize, //输出图像的大小;如果它等于零,由公式进行计算
                        //dsize=Size(round(fx*src.cols),round(fy*src.rows))
            double fx=0,//沿水平轴的缩放系数,有默认值0,且当其等于0时,由下式进行计算:
                        //dsize.width/src.cols;                   
            double fy=0,//沿垂直轴的缩放系数,有默认值0,且当其等于0时,由下式进行计算:
                       //dszie.height/src.rows;
            int interpolation=INTER_LINEAR )//用于指定插值方式,默认为INTER_LINEAR(线性插值)
             //INTER_NEAREST - 最近邻插值
            //INTER_LINEAR - 线性插值(默认值)
            //INTER_AREA - 区域插值(利用像素区域关系的重采样插值)
           //INTER_CUBIC –三次样条插值(超过4×4像素邻域内的双三次插值)
          //INTER_LANCZOS4 -Lanczos插值(超过8×8像素邻域的Lanczos插值)
//若要缩小图像,一般情况下最好用CV_INTER_AREA来插值,

//而若要放大图像,一般情况下最好用CV_INTER_CUBIC(效率不高,慢,不推荐使用)或CV_INTER_LINEAR(效率较高,速度较快,推荐使用)。

代码:

#include <opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
#include <string> 
#include<fstream> 
using namespace cv;
using namespace std;

int main() {
    Mat src, dst1,dst2;
    src = imread("C:\\Users\\Administrator\\Desktop\\pic\\5.jpg");
    imshow("input", src);
    resize(src, dst1, Size(src.cols / 2, src.rows / 2));//若要缩小图像,一般情况下最好用CV_INTER_AREA来插值
    imshow("outputdown", dst1);
    resize(src, dst2, Size(src.cols * 2, src.rows * 2), (0, 0), (0, 0),4);//若要放大图像,一般情况下最好用CV_INTER_CUBIC(效率不高,慢,不推荐使用)或CV_INTER_LINEAR(效率较高,速度较快,推荐使用)
    imshow("outputup", dst2);
    waitKey(0);
}

结果:
这里写图片描述

图像阈值(threshold):图像分割的标尺
阈值类型:
阈值二值化(threshold binary):
设定一个临界值,将像素值大于临界值的像素设置成255,低于临界值设置成0
dst(x,y)={maxVal ifsrc(x,y)>thresh
dst(x,y)={0 otherwise

阈值反二值化:
设定一个临界值,将像素值大于临界值的像素设置成0,低于临界值设置成255。
dst(x,y)={0 ifsrc(x,y)>thresh
dst(x,y)={maxVal otherwise

截断:
设定一个临界值,将像素值大于临界值的像素设置成临界值,低于临界值不变。
dst(x,y)={threshhold ifsrc(x,y)>thresh
dst(x,y)={src(x,y) otherwise

阈值取0:
设定一个临界值,将像素值大于临界值的像素不变,低于临界值设置成0。
dst(x,y)={src(x,y) ifsrc(x,y)>thresh
dst(x,y)={0 otherwise

阈值反取0:
设定一个临界值,将像素值大于临界值的像素设置成0,低于临界值不变。
dst(x,y)={0 ifsrc(x,y)>thresh
dst(x,y)={src(x,y) otherwise

代码:

#include <opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
#include <string> 
#include<fstream> 
using namespace cv;
using namespace std;

Mat src, gray_src, dst,dst1,dst2,dst3,dst4,dst5;
int threshold_value = 127;
int type_value = 1;
void getThresholdChange(int, void*) {
    cvtColor(src, gray_src, CV_RGB2GRAY);
    imshow("gray", gray_src);

    threshold(gray_src, dst1, threshold_value, 255, THRESH_BINARY);//阈值二值化
    imshow("THRESH_BINARY", dst1);

    threshold(gray_src, dst2, threshold_value, 255, THRESH_BINARY_INV);//阈值反二值化
    imshow("THRESH_BINARY_INV", dst2);

    threshold(gray_src, dst3, threshold_value, 255, THRESH_TRUNC);//截断
    imshow("THRESH_TRUNC", dst3);

    threshold(gray_src, dst4, threshold_value, 255, THRESH_TOZERO);//阈值取0
    imshow("THRESH_TOZERO", dst4);

    threshold(gray_src, dst5, threshold_value, 255, THRESH_TOZERO_INV);//阈值反取0
    imshow("THRESH_TOZERO_INV", dst5);

    threshold(gray_src, dst, threshold_value, 255, type_value);
    imshow("outputthreshold", dst);
}

int main() {
    src = imread("C:\\Users\\Administrator\\Desktop\\pic\\z2.jpg");
    imshow("input", src);
    getThresholdChange(0, 0);
    createTrackbar("阈值大小:", "outputthreshold", &threshold_value, 255, getThresholdChange);
    createTrackbar("type Value", "outputthreshold", &type_value, 5, getThresholdChange);
    waitKey(0);
}

结果:

这里写图片描述

2019-01-10 00:24:47 qq_42887760 阅读数 1565
  • 信息隐蔽

    1.使用容器平台解决了当前哪些实际的问题2.为后来者做容器选型提供参考建议3.通过技术复盘,整理一些具体问题的解决方案,少走弯路

    11人学习 任铄
    免费试看

图像金字塔概念

  1. 我们在图像处理中常常会调整图像大小,最常见的就是放大(zoom in)和缩小(zoom out),尽管几何变换也可以实现图像放大和缩小,但是这里我们介绍图像金字塔
  2. 一个图像金字塔式一系列的图像组成,最底下一张是图像尺寸最大,最上方的图像尺寸最小,从空间上从上向下看就想一个古代的金字塔。
    在这里插入图片描述
  • 高斯金子塔 – 用来对图像进行降采样
  • 拉普拉斯金字塔 – 用来重建一张图片根据它的上层降采样图片(分辨率较低)

图像金字塔概念 – 高斯金字塔

  • 高斯金子塔是从底向上,逐层降采样得到。
  • 降采样之后图像大小是原图像MxN的M/2 x N/2 ,就是对原图像删除偶数行与列,即得到降采样之后上一层的图片。
  • 高斯金子塔的生成过程分为两步:
    • 1.对当前层进行高斯模糊
    • 2.删除当前层的偶数行与列
      即可得到上一层的图像,这样上一层跟下一层相比,都只有它的1/4大小。
      在这里插入图片描述

高斯不同(Difference of Gaussian-DOG)

  • 定义:就是把同一张图像在不同的参数下做高斯模糊之后的结果相减,得到的输出图像。称为高斯不同(DOG)
  • 高斯不同是图像的内在特征,在灰度图像增强、角点检测中经常用到。

采样相关API

  • 上采样(cv::pyrUp) – zoom in 放大
    pyrUp(Mat src, Mat dst, Size(src.cols2, src.rows2))
    生成的图像是原图在宽与高各放大两倍

  • 降采样 (cv::pyrDown) – zoom out 缩小
    pyrDown(Mat src, Mat dst, Size(src.cols/2, src.rows/2))
    生成的图像是原图在宽与高各缩小1/2

代码案例:

#include<opencv2/opencv.hpp>
#include<iostream>

using namespace cv;

int main(){
	//1. 加载图像和显示原图
	Mat src=imread("E:/Experiment/OpenCV/Pictures/dog2.jpg");
	if(src.empty()){
		printf("could not load image...");
		return -1;
	}
	namedWindow("input_windows",CV_WINDOW_AUTOSIZE);
	imshow("input_windows",src);//显示图片

	//2. 创建两个Mat对象来存储操作的结果,并执行采样操作
	Mat dst1,dst2;
	//2.1. 上采样
	pyrUp(src, dst1, Size(src.cols * 2, src.rows * 2));//上采样,生成的图像是原图在宽与高各放大两倍
    imshow("pyrUp", dst1);
	//2.2. 下采样
    pyrDown(src, dst2, Size(src.cols / 2, src.rows / 2));//降采样,生成的图像是原图在宽与高各缩小1/2
    imshow("pyrDown", dst2);


	//高斯不同
	Mat gray_src,gblur1,gblur2,gblur3,dogImg,dogImg2,dogImg3;
	cvtColor(src, gray_src, COLOR_BGR2GRAY);

	GaussianBlur(gray_src, gblur1, Size(3, 3), 0, 0);
    GaussianBlur(gblur1, gblur2, Size(3, 3), 0, 0);//方式一
    subtract(gblur1, gblur2, dogImg, Mat());//获取 DOG ,应该要低的gblur1减高的gblur2
    //将灰度图的0-255空间,转换到0-1空间(二值空间),让原本淡淡的图像变得清楚了
    normalize(dogImg, dogImg, 255, 0, NORM_MINMAX);//参数:255最大值,0最小值,NORM_MINMAX 最大最小化
    imshow("dogImg", dogImg);//能看到淡淡的猫的眼睛的轮廓
	GaussianBlur(gray_src, gblur3, Size(5, 5), 0, 0);
    subtract(gblur1, gblur3, dogImg2, Mat());//方式二
    normalize(dogImg2, dogImg2, 255, 0, NORM_MINMAX);
    imshow("dogImg2", dogImg2);//也能看到淡淡的猫的眼睛的轮廓

    subtract(gblur2, gblur1, dogImg3, Mat());//高减低的
    normalize(dogImg3, dogImg3, 255, 0, NORM_MINMAX);
    imshow("dogImg3", dogImg3);

	waitKey(0);
	return 0;
}

运行截图:

在这里插入图片描述
在这里插入图片描述

小案例:

1. 加载图片
2. 进行上采样(按’u’后):
3. 执行下采样(按’d’后):
4. 退出程序(按’Esc’后):

#include<opencv2/opencv.hpp>
#include<iostream>

using namespace cv;


int main(){
	//1. 加载图像和显示原图
	Mat src=imread("E:/Experiment/OpenCV/Pictures/dog2.jpg");
	if(src.empty()){
		printf("could not load image...");
		return -1;
	}
	namedWindow("input_windows",CV_WINDOW_AUTOSIZE);
	imshow("input_windows",src);//显示图片
	
	Mat temp,dst;
	temp=src;
	dst=temp;
	while(true){
		int c;
		c = waitKey(10);
		if( (char)c == 27 ){//Esc键
			break;
		}
		else if( (char)c == 'u' ){ 
			pyrUp( temp, dst, Size( temp.cols*2, temp.rows*2 ) );
			printf( "** Zoom In: Image x 2 \n" );
		}
		else if( (char)c == 'd' )
		{ 
			pyrDown( temp, dst, Size( temp.cols/2, temp.rows/2 ) );
			printf( "** Zoom Out: Image / 2 \n" );
		}
		imshow( "output_windows", dst );
		temp = dst;
	}

	waitKey(0);
	return 0;
}

推荐博客

  1. https://blog.csdn.net/LYKymy/article/details/83153927
  2. https://blog.csdn.net/huanghuangjin/article/details/80958983
2017-08-21 23:01:26 licaizi1025365743 阅读数 10629
  • 信息隐蔽

    1.使用容器平台解决了当前哪些实际的问题2.为后来者做容器选型提供参考建议3.通过技术复盘,整理一些具体问题的解决方案,少走弯路

    11人学习 任铄
    免费试看

此篇文章是关于图像降采样的实现,侧重点是为了详细阐述降采样的实现,而无关乎优化,代码基于OpenCV 3.2.0&&C++实现。降采样算法主要采用高斯卷积实现,卷积核采用一维卷积核:double w[5] = {1.0/4 - a/2.0, 1.0/4, a, 1.0/4, 1.0/4 - a/2.0}, 取a = 0.6,降采样主题思想如下图所示(图像从UCF大学计算机视觉课件上截取):
这里写图片描述
这里写图片描述

以下是代码实现:

void myPyrDown(Mat img, Mat &dst)
{
    // 降采样高斯权重
    double a = 0.6;
    double w[5] = {1.0/4 - a/2.0, 1.0/4, a, 1.0/4, 1.0/4 - a/2.0};// 这里面double类型需要1.0/4而不是1/4,我有时会疏忽这一点
    // 转换图像数据类型
    Mat src = img.clone();
    src.convertTo(src, CV_64FC1);
    // 定义目标矩阵,矩阵行列数减半向上取整(/2.0而不是/2)
    dst = Mat((int)(src.rows / 2.0 + 0.5), (int)(src.cols / 2.0 + 0.5), CV_64FC1, Scalar(0.0));
    // 定义x方向降采样临时矩阵
    Mat temp_x(src.rows, (int)(src.cols / 2.0 + 0.5), CV_64FC1, Scalar(0.0));
    // 定义x方向边界扩充,两边扩充的两列像素直接复制原图像两边缘像素
    Mat paddImg_x(src.rows, src.cols + 4, CV_64FC1, Scalar(0.0));
    for (int i = 0; i < paddImg_x.rows; ++i)
    {
        for (int j = 0; j < paddImg_x.cols; ++j)
        {
            if (j < 2)
                paddImg_x.at<double>(i, j) = src.at<double>(i, j);
            else if (j >= 2 && j < paddImg_x.cols - 2)
                paddImg_x.at<double>(i, j) = src.at<double>(i, j - 2);
            else
                paddImg_x.at<double>(i, j) = paddImg_x.at<double>(i, j - 1);
        }
    }
    // x方向降采样
    for (int i = 0; i < temp_x.rows; ++i)
    {
        for (int j = 0; j < temp_x.cols; ++j)
        {
            for (int m = -2; m <= 2; ++m)
            {
                // 权重和
                temp_x.at<double>(i, j) += w[m + 2] * paddImg_x.at<double>(i, j * 2 + m + 2);
            }
        }
    }
    // 基于已得到的temp_x,定义y方向边界扩充矩阵
    Mat paddImg_y(temp_x.rows + 4, temp_x.cols, CV_64FC1, Scalar(0.0));
    for (int i = 0; i < paddImg_y.rows; ++i)
    {
        for (int j = 0; j < paddImg_y.cols; ++j)
        {
            if (i < 2)
                paddImg_y.at<double>(i, j) = temp_x.at<double>(i, j);
            else if (i >= 2 && i < paddImg_y.rows - 2)
                paddImg_y.at<double>(i, j) = temp_x.at<double>(i - 2, j);
            else
                paddImg_y.at<double>(i, j) = paddImg_y.at<double>(i - 1, j);
        }
    }
    // y方向上的降采样
    for (int i = 0; i < dst.rows; ++i)
    {
        for (int j = 0; j < dst.cols; ++j)
        {
            for (int m = -2; m <= 2; ++m)
            {
                // 权重和
                dst.at<double>(i, j) += w[m + 2] * paddImg_y.at<double>(i * 2 + m + 2, j);
            }
        }
    }
    // 转换图像数据类型
    dst.convertTo(dst, CV_8U);
}

以下为测试代码:

#include <iostream>
#include <vector>
#include <cmath>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

void myPyrDown(Mat img, Mat &dst);

int main()
{
    Mat img1 = imread("lena.tiff", IMREAD_GRAYSCALE);
    Mat img2;
    myPyrDown(img1, img2);

    imshow("img1", img1);
    imshow("img2", img2);
    waitKey(0);
    return 0;
}

以下为测试结果(图像大小为511*511):
这里写图片描述
这里写图片描述