实验E2:图像代数运算
实验2.1:对比度调整
设计一个Sigmoid函数,实现对图像的对比度调整,并使得调整幅度可以通过参数控制;
通过查阅资料得知,Sigmoid函数的标准形式为:
其中,
x 表示输入的像素值,并且可以归一化到[0,1]范围内。
k 表示的是控制对比度调整幅度的参数,k 值越大,图像的对比度越高;k 值越小,对比度越低。
x0 参数用来调整Sigmoid函数的中心点,通过查阅资料可知该参数的值一般取 0.5 。
据此编写出Sigmoid函数的代码:
inline float sigmoid(float x,float k,float x0){return 1.0/(1.0+exp(-k*(x-x0)));
}
利用Sigmoid函数调整图像对比度的代码如下:
cv::Mat contrast_adjust(const cv::Mat& src,float k,float x0){cv::Mat dst=cv::Mat::zeros(src.size(),src.type());for(int i=0;i<src.rows;++i){for(int j=0;j<src.cols;++j) {for (int c=0;c<src.channels();++c) {//归一化到[0,1]float pixel=src.at<cv::Vec3b>(i,j)[c]/255.0;float adjusted=sigmoid(pixel,k,x0);//映射回[0,255]dst.at<cv::Vec3b>(i,j)[c]=cv::saturate_cast<uchar>(adjusted*255);}}}return dst;
}
测试结果如下:
当k=3,x0=0.5时,原图(上)和调整后的图片(下)的对比如下:
当k=80,x0=0.4时,图像的对比如下:
实验2.2:背景相减
•分析你的方法可能产生误检的情况,并上网查阅背景相减的改进方法,设法改进结果。
int main(int argc,char *argv[]){cv::Mat fimg=cv::imread("2-2_f.png");cv::Mat bimg=cv::imread("2-2_b.png");if(fimg.empty()||bimg.empty()||fimg.rows!=bimg.rows||fimg.cols!=bimg.cols){std::cout<<"Illegal Input!"; return 0;}cv::Mat res=cv::Mat::zeros(fimg.size(),fimg.type());for(int i=0;i<fimg.rows;++i){for(int j=0;j<fimg.cols;++j){double dis=0; //距离for(int k=0;k<3;++k){dis+=std::pow(fimg.at<cv::Vec3b>(i,j)[k]-bimg.at<cv::Vec3b>(i,j)[k],2);}if(dis<5000) res.at<cv::Vec3b>(i,j)={0,0,0};else res.at<cv::Vec3b>(i,j)=fimg.at<cv::Vec3b>(i,j);}}cv::imshow("output",res);cv::waitKey(0);cv::destroyAllWindows();return 0;
}
当阈值分别取100,1000,5000,10000时,实验结果如下:
可以看到,朴素算法的处理结果中要么去除不干净背景,要么就连背景和需要保留的人一起去除了,总之效果不是太好。
因此又去寻找有关背景相减的改进算法,了解到了一种比较通用的处理算法:
仔细研究后发现这个方法好像和我的朴素算法基本一样,因此又尝试在这个算法的基础上进行改进。想到可以对每一个像素点和它周围的几个像素点做平均,利用平均后的值再进行上面的步骤,这次的实验结果如下:
实验代码
int main(int argc,char *argv[]){cv::Mat fimg=cv::imread("2-2_f.png");cv::Mat bimg=cv::imread("2-2_b.png");if(fimg.empty()||bimg.empty()||fimg.rows!=bimg.rows||fimg.cols!=bimg.cols){std::cout<<"Illegal Input!"; return 0;}cv::Mat res=cv::Mat::zeros(fimg.size(),fimg.type());int max_step=1;int dx[]={-1,-1,0,1,1,1,0,-1},dy[]={0,1,1,1,0,-1,-1,-1};for(int i=0;i<fimg.rows;++i){for(int j=0;j<fimg.cols;++j){double dis=0; //距离cv::Vec3b avg=bimg.at<cv::Vec3b>(i,j);//std::cout<<avg<<"|";int avg_cnt=1;for(int step=1;step<=max_step;++step){for(int twd=0;twd<8;++twd){int ni=i+dx[twd]*step,nj=j+dy[twd]*step;if(ni<0||ni>=fimg.rows||nj<0||nj>=fimg.cols) continue;++avg_cnt;avg+=bimg.at<cv::Vec3b>(ni,nj);}}avg=avg/avg_cnt;//std::cout<<avg<<std::endl;cv::Vec3b avgf=fimg.at<cv::Vec3b>(i,j);//std::cout<<avg<<"|";int avg_cntf=1;for(int step=1;step<=max_step;++step){for(int twd=0;twd<8;++twd){int ni=i+dx[twd]*step,nj=j+dy[twd]*step;if(ni<0||ni>=fimg.rows||nj<0||nj>=fimg.cols) continue;++avg_cntf;avgf+=fimg.at<cv::Vec3b>(ni,nj);}}avgf=avgf/avg_cntf;for(int k=0;k<3;++k){dis+=std::pow(avgf[k]-avg[k],2);}if(dis<1) res.at<cv::Vec3b>(i,j)={0,0,0};else res.at<cv::Vec3b>(i,j)=fimg.at<cv::Vec3b>(i,j);}}cv::imshow("output",res);cv::waitKey(0);cv::destroyAllWindows();return 0;
}
可以看到,效果还是不理想。
之后又想到了一种优化方法:在朴素算法计算完成后再对结果图遍历一遍,对每个像素点计算它周围有多少个被删除了的点和有多少个没被删除的点,如果被删除的点的比例高于某个阈值,就把它也删除掉;如果它周围没被删除的点的比例高于某个阈值,就把前景图中对应点的像素赋给它。此方法的实验代码如下:
bool del_map[5000][5000],rec_map[5000][5000];
int main(int argc,char *argv[]){cv::Mat fimg=cv::imread("2-2_f.png");cv::Mat bimg=cv::imread("2-2_b.png");if(fimg.empty()||bimg.empty()||fimg.rows!=bimg.rows||fimg.cols!=bimg.cols){std::cout<<"Illegal Input!"; return 0;}cv::Mat res=cv::Mat::zeros(fimg.size(),fimg.type());for(int i=0;i<fimg.rows;++i){for(int j=0;j<fimg.cols;++j){double dis=0; //距离for(int k=0;k<3;++k){dis+=std::pow(fimg.at<cv::Vec3b>(i,j)[k]-bimg.at<cv::Vec3b>(i,j)[k],2);}if(std::sqrt(dis)<100) res.at<cv::Vec3b>(i,j)={0,0,0};else res.at<cv::Vec3b>(i,j)=fimg.at<cv::Vec3b>(i,j);}}cv::imshow("ori",res);int max_step=10;int dx[]={-1,-1,0,1,1,1,0,-1},dy[]={0,1,1,1,0,-1,-1,-1};double erase_rate=0.1,recover_rate=0.2;for(int i=0;i<fimg.rows;++i){for(int j=0;j<fimg.cols;++j){int tot=0,del_cnt=0;for(int step=1;step<=max_step;++step){for(int twd=0;twd<8;++twd){int ni=i+dx[twd]*step,nj=j+dy[twd]*step;if(ni<0||ni>=fimg.rows||nj<0||nj>=fimg.cols) continue;++tot;if(res.at<cv::Vec3b>(ni,nj)==cv::Vec3b{0,0,0}) ++del_cnt;}}if(del_cnt*1.0/tot>=erase_rate) del_map[i][j]=true;if((tot-del_cnt)*1.0/tot>=recover_rate) rec_map[i][j]=true;}}for(int i=0;i<fimg.rows;++i){for(int j=0;j<fimg.cols;++j){if(del_map[i][j]) res.at<cv::Vec3b>(i,j)={0,0,0};if(rec_map[i][j]) res.at<cv::Vec3b>(i,j)=fimg.at<cv::Vec3b>(i,j);}}cv::imshow("output",res);cv::waitKey(0);cv::destroyAllWindows();return 0;
}
当阈值取100,删除比例取0.1,恢复比例取0.2时,原方法(左)和改进方法(右)的效果如下:
可以看到,优化后的方法在效果上对比原方法还是有一定的改进的。