【C++】浅谈代码效率优化

前言

最近因为一些特殊原因,需要写几个图像处理相关的代码。计划手动实现双边滤波,目的是熟悉opencv对图像的存储方式,以及相关的库函数的使用。

在不考虑效率,使用Mat提供的成员函数以及vector等容器实现功能后,100W的像素跑出了7分钟,也着实让我吓了一跳。

最终,进过优化,100W像素跑到3-4秒之间。这个效率,说实话,并不是特别满意。理想状态应该秒开,毫无延迟的感觉。要达到毫无延迟,要么使用快速滤波算法,要么进一步优化代码。而我,就是想将代码写到极致。

优化之前的源代码

双边滤波器原理请点这里


inline void getColorWeightBuffer(double* CWBuffer, double sigma) {
	double temp_ = -0.5 / (sigma * sigma);
	for (int i = 0; i < 768; i++) {
		CWBuffer[i] = exp(i * i * temp_);
	}
}
inline void getSpaceWeightBuffer(Mat &mask, double sigma, int ksize) {
	double temp_ = -0.5 / (sigma * sigma);
	int radius = ksize / 2;
	int distance = 0;
	for (int i = -radius; i <= radius; i++) {
		double* rows = mask.ptr<double>(i+radius);
		for (int j = -radius; j <= radius; j++) {
			distance = i * i + j * j;
			rows[j + radius] = exp(distance * temp_);
		}
	}

}
void myBilateralFilters(Mat& src, Mat& dst,int ksize , double space_sigma,double color_sigma) {
	CV_DbgAssert(src.channels() == 1 || src.channels() == 3);
	unique_ptr<double> CWBuffer(new double[256]);
	Mat mask(ksize, ksize, CV_64F);
	getColorWeightBuffer(CWBuffer.get(), color_sigma);
	getSpaceWeightBuffer(mask, space_sigma, ksize);
	int radius = ksize / 2;
	dst.create(src.size(), src.type());
	for (int i = 0; i < src.rows ; i++) {              // #1
		uchar* ptr = src.ptr<uchar>(i);                // #2  
		for (int j = 0; j < src.cols; j++) {           // #3
			double sum[3]{ 0 };                
			double w[3]{ 0 };                  
			double graydiff[3]{ 0 };           
			for (int k = 0; k < ksize; k++) {          // #3
				for (int l = 0; l < ksize; l++) {
					if (i - radius + k < 0 || j - radius + l < 0 || i -radius + k > src.rows-1 || j - radius + l > src.cols-1) continue;  // #4
					if (src.channels() == 1) {            //#5
						w[0] = CWBuffer.get()[abs(ptr[j] - src.at<uchar(i-radius+k,j-radius+k))] * mask.at<double>(k,l);
						sum[0] += w[0];
						graydiff[0] += src.at<uchar>(i - radius + k, j -radius + k) * w[0];
					}
					else if (src.channels() == 3) {
						w[0] = CWBuffer.get()[abs(ptr[j*src.channels()] - srcat<Vec3b>(i-radius+k,j-radius+l)[0])] * maskat<double>(k,l);          // #6
						w[1] = CWBuffer.get()[abs(ptr[j*src.channels() + 1] -src.at<Vec3b>(i-radius+k,j-radius+l)[1])] * maskat<double>(k,l);
						w[2] = CWBuffer.get()[abs(ptr[j*src.channels() + 2] -src.at<Vec3b>(i-radius+k,j-radius+l)[2])] * maskat<double>(k,l);
						sum[0] += w[0];
						sum[1] += w[1];
						sum[2] += w[2];
						graydiff[0] += src.at<Vec3b>(i - radius + k, j -radius + l)[0] * w[0];       // #7
						graydiff[1] += src.at<Vec3b>(i - radius + k, j -radius + l)[1] * w[1];
						graydiff[2] += src.at<Vec3b>(i - radius + k, j -radius + l)[2] * w[2];
					}
				}
			}
			if (dst.channels() == 1) {
				dst.at<uchar>(i, j) = graydiff[0] / sum[0];
			}
			else if (dst.channels() == 3) {
				dst.at<Vec3b>(i, j)[0] = graydiff[0] / sum[0];            //#8
				dst.at<Vec3b>(i, j)[1] = graydiff[1] / sum[1];
				dst.at<Vec3b>(i, j)[2] = graydiff[2] / sum[2];
			}
		}
	}
}

这里实现的双边滤波和原来的还是有点不一样的地方。原版双边滤波器算像素权值,是将三个通道的差加在一起作为计算像素权重的参数,这边是将三个通道单独计算的。从效果上来说,原版的包边性更好。当然,后边优化的时候,为了和原版保持一致,改回和原版一样了。

优化原理

  1. 调用函数的时间开销,远远大于使用局部变量

  2. 通过指针修改数据,效率远比通过Mat对象提供的成员函数快

  3. 减少大量的重复计算。

  4. 慎用STL模板(效率真的不咋地)

具体方案

  • 获取指向图像数据的指针,通过移动指针更改数据。

    Mat存数数据的结构看这篇博客,图文结合,非常好理解。

    	uchar *data = src.data;
    	//访问单通道(i,j)得像素
    	data[i*cols+j];
    	//访问三通道图
    	b = data[((i*cols)+j)*channels]  // channels = 3
    	g = data[((i*cols)+j)*channels + 1]  // channels = 3
    	r = data[((i*cols)+j)*channels + 2]  // channels = 3
    
  • 将需要大量使用的参数,赋值给局部变量,避免过多的函数调用

    int channels = src.channels();  //这里只是个例子
    int rows = src.rows;
    int cols = src.cols;
    

    这里不得不提一嘴,把循环体中的channels换了之后,效率有了质的飞跃。

优化后代码

void myBilateralFilters2(Mat& src, Mat& dst, int ksize, double space_sigma, double color_sigma) {
	CV_DbgAssert(src.channels() == 1 || src.channels() == 3);
	int channels = src.channels();
	double* CWBuffer = new double[768];
	getColorWeightBuffer(CWBuffer, color_sigma);
	Mat mask(ksize, ksize, CV_64F);
	getSpaceWeightBuffer(mask, space_sigma, ksize);
	uchar* umask = mask.data;
	int radius = ksize / 2;
	dst.create(src.size(), src.type());
	uchar* udst = dst.data;
	uchar* usrc = src.data;
	int rows = src.rows;
	int cols = src.cols;
	for (int i = 0; i < rows; i++) {
		for (int j = 0; j < cols; j++) {
			double sum[3]{ 0 };                //归一化权重
			double w[3]{ 0 };                  // 权重
			double graydiff[3]{ 0 };
			int LT = i - radius;
			int RT = j - radius;
			int LB = i - radius - src.rows + 1;
			int RB = j - radius - src.cols + 1;
			for (int k = 0; k < ksize; k++) {
				for (int l = 0; l < ksize; l++) {
					if (LT + k < 0 || RT + l < 0 || LB > -k || RB > -l) continue;
					if (channels == 1) {
						uchar gray = usrc[(LT + k) * cols + RT + l];
						w[0] = CWBuffer[abs(usrc[i*cols+j] - gray)] * umask[k * ksize + l];
						sum[0] += w[0];
						graydiff[0] += gray * w[0];
					}
					else if (channels== 3) {
						int index = ((LT + k) * cols + (RT + l)) * 3;
						int index_gray = (i * cols + j) * 3;
						int index_umask = k * ksize + l;
						int color_ = abs(usrc[index_gray] - usrc[index]) + abs(usrc[index_gray + 1] - usrc[index + 1]) + abs(usrc[index_gray + 2] - usrc[index + 2]);
						w[0] = CWBuffer[color_] * umask[index_umask];
						w[1] = CWBuffer[color_] * umask[index_umask];
						w[2] = CWBuffer[color_] * umask[index_umask];
						/*
						w[0] = CWBuffer[abs(usrc[index_gray] - usrc[index])] * umask[index_umask];
						w[1] = CWBuffer[abs(usrc[index_gray + 1] - usrc[index + 1])] * umask[index_umask];
						w[2] = CWBuffer[abs(usrc[index_gray + 2] - usrc[index + 2])] * umask[index_umask];
						*/
						sum[0] += w[0];
						sum[1] += w[1];
						sum[2] += w[2];
						graydiff[0] += usrc[index] * w[0];
						graydiff[1] += usrc[index + 1] * w[1];
						graydiff[2] += usrc[index + 2] * w[2];
					}
				}
			}
			if (channels == 1) {
				udst[i * cols + j] = graydiff[0] / sum[0];
			}
			else if (channels == 3) {
				udst[(i * dst.cols + j) * 3] = graydiff[0] / sum[0];
				udst[(i * dst.cols + j) * 3 + 1] = graydiff[1] / sum[1];
				udst[(i * dst.cols + j) * 3 + 2] = graydiff[2] / sum[2];
			}
		}	
	}

	delete[] CWBuffer;

}

总结

上述优化,完全是根据个人编程经验改的。如果有更好的修改方案,希望各位大佬能够指导。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值