实验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:背景相减
•对图像I和对应的背景图B,基于背景相减检测I中的前景区域,并输出前景的mask.
•分析你的方法可能产生误检的情况,并上网查阅背景相减的改进方法,设法改进结果。
•测试数据见群文件bgs-data.zip
首先能够自然而然地想到一种朴素的方法:遍历前景图和背景图中的每一个点,计算对应像素点的每一种颜色数值之间的差的平方和,即它们之间的欧氏距离。如果这个距离小于某个阈值那么就认为这两个像素之间是一样的,并在结果图中去掉这个像素点。利用这个朴素算法进行背景相减的实验代码如下:
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时,原方法(左)和改进方法(右)的效果如下:
可以看到,优化后的方法在效果上对比原方法还是有一定的改进的。
标签:bimg,Mat,int,数字图像处理,cols,实验,fimg,cv From: https://www.cnblogs.com/Nartsam/p/18417558