陷波濾波器是更有用的選擇性濾波器。陷波濾波器拒絕事先定義的關於矩形中心的一個鄰域的頻率。
零相移濾波器必須是關於原點對稱的,因此一箇中心位於(u0,v0)的陷波在位置(-u0,-v0)必須有一個對應的陷波。
陷波帶阻濾波器可以用中心已被平移到陷波濾波器中心的高通濾波器的乘積來構造。一般形式爲:
其中,是高通濾波器,他們的中心分別位於(u_k, v_k)和(-u_k, -v_k);這些中心是根據頻率矩形的中心(M/2,N/2)確定的。對於每個濾波器,距離計算由下式執行:
例如下面是一個n階布特沃斯陷波帶阻濾波器,它包含三個陷波對;
和 由 上式計算得出; 常數 對每一個陷波對都是相同的。對於不同陷波對可以不同。其他陷波帶阻濾波器可用相同方法構建。
前面說過,1-帶阻濾波就是帶通,因此陷波帶通濾波器:
陷波濾波是選擇性的修改DFT的局部區域。典型處理是交互式完成,它直接對DFT處理,
下面使用陷波濾波減少圖像的摩爾波紋。
使用布特沃斯 n=4,D0=30處理
原圖像 ---------------------- 處理後圖像
原圖像頻譜圖 --------------------------------------- 處理後圖像頻譜圖
陷波濾波器
通過鼠標選擇矩形區域,找出最大值點,當做(u,v);
代碼實現:
#include "opencv2/opencv.hpp"
typedef cv::Mat Mat;
Mat image_add_border( Mat &src )
{
int w=2*src.cols;
int h=2*src.rows;
std::cout << "src: " << src.cols << "*" << src.rows << std::endl;
cv::Mat padded;
copyMakeBorder( src, padded, 0, h-src.rows, 0, w-src.cols,
cv::BORDER_CONSTANT, cv::Scalar::all(0));
padded.convertTo(padded,CV_32FC1);
std::cout << "opt: " << padded.cols << "*" << padded.rows << std::endl;
return padded;
}
//transform to center 中心化
void center_transform( cv::Mat &src )
{
for(int i=0; i<src.rows; i++){
float *p = src.ptr<float>(i);
for(int j=0; j<src.cols; j++){
p[j] = p[j] * pow(-1, i+j);
}
}
}
//對角線交換內容
void zero_to_center(cv::Mat &freq_plane)
{
// freq_plane = freq_plane(Rect(0, 0, freq_plane.cols & -2, freq_plane.rows & -2));
//這裏爲什麼&上-2具體查看opencv文檔
//其實是爲了把行和列變成偶數 -2的二進制是11111111.......10 最後一位是0
int cx=freq_plane.cols/2;int cy=freq_plane.rows/2;//以下的操作是移動圖像 (零頻移到中心)
cv::Mat part1_r(freq_plane, cv::Rect(0,0,cx,cy)); //元素座標表示爲(cx,cy)
cv::Mat part2_r(freq_plane, cv::Rect(cx,0,cx,cy));
cv::Mat part3_r(freq_plane, cv::Rect(0,cy,cx,cy));
cv::Mat part4_r(freq_plane, cv::Rect(cx,cy,cx,cy));
cv::Mat tmp;
part1_r.copyTo(tmp); //左上與右下交換位置(實部)
part4_r.copyTo(part1_r);
tmp.copyTo(part4_r);
part2_r.copyTo(tmp); //右上與左下交換位置(實部)
part3_r.copyTo(part2_r);
tmp.copyTo(part3_r);
}
void show_spectrum( cv::Mat &complexI )
{
cv::Mat temp[] = {cv::Mat::zeros(complexI.size(),CV_32FC1),
cv::Mat::zeros(complexI.size(),CV_32FC1)};
//顯示頻譜圖
cv::split(complexI, temp);
cv::Mat aa;
cv::magnitude(temp[0], temp[1], aa);
// zero_to_center(aa);
cv::divide(aa, aa.cols*aa.rows, aa);
double min, max;
cv::Point min_loc, max_loc;
cv::minMaxLoc(aa, &min, &max, &min_loc, &max_loc );
std::cout << "min: " << min << " max: " << max << std::endl;
std::cout << "min_loc: " << min_loc << " max_loc: " << max_loc << std::endl;
cv::circle( aa, min_loc, 20, cv::Scalar::all(1), 3);
cv::circle( aa, max_loc, 20, cv::Scalar::all(1), 3);
cv::imshow("src_img_spectrum",aa);
}
//頻率域濾波
cv::Mat frequency_filter(cv::Mat &padded,cv::Mat &blur)
{
cv::Mat plane[]={padded, cv::Mat::zeros(padded.size(), CV_32FC1)};
cv::Mat complexIm;
cv::merge(plane,2,complexIm);
cv::dft(complexIm,complexIm);//fourior transform
show_spectrum(complexIm);
cv::multiply(complexIm, blur, complexIm);
cv::idft(complexIm, complexIm, CV_DXT_INVERSE); //idft
cv::Mat dst_plane[2];
cv::split(complexIm, dst_plane);
center_transform(dst_plane[0]);
// center_transform(dst_plane[1]);
cv::magnitude(dst_plane[0],dst_plane[1],dst_plane[0]); //求幅值(模)
// center_transform(dst_plane[0]); //center transform
return dst_plane[0];
}
//陷波濾波器
cv::Mat notch_kernel( cv::Mat &scr, std::vector<cv::Point> ¬ch_pot, float D0 )
{
cv::Mat notch_pass(scr.size(),CV_32FC2);
int row_num = scr.rows;
int col_num = scr.cols;
int n = 4;
for(int i=0; i<row_num; i++ ){
float *p = notch_pass.ptr<float>(i);
for(int j=0; j<col_num; j++ ){
float h_nr = 1.0;
for( unsigned int k = 0; k < notch_pot.size(); k++ ){
int u_k = notch_pot.at(k).y;
int v_k = notch_pot.at(k).x;
float dk = sqrt( pow((i-u_k),2) +
pow((j-v_k),2) );
float d_k = sqrt( pow((i+u_k),2) +
pow((j+v_k),2) );
float dst_dk = 1.0/(1.0 + pow(D0/dk, 2*n));
float dst_d_k = 1.0/(1.0 + pow(D0/d_k, 2*n));
h_nr = dst_dk * dst_d_k * h_nr;
// std::cout << "notch_pot: " << notch_pot.at(k) << std::endl;
}
p[2*j] = h_nr;
p[2*j+1] = h_nr;
}
}
cv::Mat temp[] = { cv::Mat::zeros(scr.size(), CV_32FC1),
cv::Mat::zeros(scr.size(), CV_32FC1) };
cv::split(notch_pass, temp);
std::string name = "notch濾波器d0=" + std::to_string(D0);
cv::Mat show;
cv::normalize(temp[0], show, 1, 0, CV_MINMAX);
cv::imshow(name, show);
return notch_pass;
}
std::string name_win("Notch_filter");
cv::Rect g_rectangle;
bool g_bDrawingBox = false;//是否進行繪製
cv::RNG g_rng(12345);
std::vector<cv::Point> notch_point;
void on_MouseHandle(int event, int x, int y, int flags, void*param);
void DrawRectangle(cv::Mat& img, cv::Rect box);
int main(int argc, char * argv[])
{
if( argc != 2){
std::cerr << "Usage: " << argv[0] << " <img_name> " << std::endl;
return -1;
}
Mat srcImage = cv::imread(argv[1], cv::IMREAD_GRAYSCALE);
if( srcImage.empty() )
return -1;
imshow( "src_img", srcImage );
Mat padded = image_add_border(srcImage);
center_transform( padded );
Mat plane[]={padded, cv::Mat::zeros(padded.size(), CV_32FC1)};
Mat complexIm;
merge(plane,2,complexIm);
cv::dft(complexIm,complexIm);//fourior transform
Mat temp[] = {cv::Mat::zeros(complexIm.size(),CV_32FC1),
cv::Mat::zeros(complexIm.size(),CV_32FC1)};
//顯示頻譜圖
split(complexIm, temp);
Mat aa;
magnitude(temp[0], temp[1], aa);
divide(aa, aa.cols*aa.rows, aa);
cv::namedWindow(name_win);
cv::imshow(name_win,aa);
cv::setMouseCallback(name_win, on_MouseHandle, (void*)&aa);
Mat tempImage = aa.clone();
int key_value = -1;
while (1){
key_value = cv::waitKey(10);
if( key_value == 27 ){ //esc key,break
break;
}
}
if( !notch_point.empty() ){
for( unsigned int i = 0; i < notch_point.size(); i++ ){
cv::circle( tempImage, notch_point.at(i), 3, cv::Scalar(1), 2);
std::cout << notch_point.at(i) << std::endl;
}
}
cv::imshow(name_win, tempImage);
Mat ker = notch_kernel( complexIm,notch_point, 30 );
cv::multiply(complexIm, ker, complexIm);
split(complexIm, temp);
magnitude(temp[0], temp[1], aa);
divide(aa, aa.cols*aa.rows, aa);
imshow( "aa", aa );
cv::idft(complexIm, complexIm, CV_DXT_INVERSE); //idft
cv::Mat dst_plane[2];
cv::split(complexIm, dst_plane);
center_transform(dst_plane[0]);
// center_transform(dst_plane[1]);
// cv::magnitude(dst_plane[0],dst_plane[1],dst_plane[0]); //求幅值(模)
cv::normalize(dst_plane[0], dst_plane[0], 1, 0, CV_MINMAX);
imshow( "dest", dst_plane[0] );
cv::waitKey(0);
return 1;
}
void on_MouseHandle(int event, int x, int y, int falgs, void* param)
{
Mat& image = *(cv::Mat*)param;
switch (event){ //鼠標移動消息
case cv::EVENT_MOUSEMOVE:{
if (g_bDrawingBox){ //標識符爲真,則記錄下長和寬到Rect型變量中
g_rectangle.width = x - g_rectangle.x;
g_rectangle.height = y - g_rectangle.y;
}
}
break;
case cv::EVENT_LBUTTONDOWN:{
g_bDrawingBox = true;
g_rectangle = cv::Rect(x, y, 0, 0);//記錄起點
std::cout << "start point( " << g_rectangle.x << "," << g_rectangle.y << ")" << std::endl;
}
break;
case cv::EVENT_LBUTTONUP: {
g_bDrawingBox = false;
bool w_less_0 = false, h_less_0 = false;
if (g_rectangle.width < 0){ //對寬高小於0的處理
g_rectangle.x += g_rectangle.width;
g_rectangle.width *= -1;
w_less_0 = true;
}
if (g_rectangle.height < 0){
g_rectangle.y += g_rectangle.height;
g_rectangle.height *= -1;
h_less_0 = true;
}
std::cout << "end Rect[ " << g_rectangle.x << "," << g_rectangle.y << "," << g_rectangle.width<< ","
<< g_rectangle.height << "]" <<std::endl;
if( g_rectangle.height > 0 && g_rectangle.width > 0 ){
Mat imageROI = image(g_rectangle).clone();
double min, max;
cv::Point min_loc, max_loc;
cv::minMaxLoc( imageROI, &min, &max, &min_loc, &max_loc);
cv::circle(imageROI, max_loc, 10, 1);
max_loc.x += g_rectangle.x;
max_loc.y += g_rectangle.y;
notch_point.push_back(max_loc);
cv::circle(image, max_loc, 10, 1);
// cv::imshow( "ROI", imageROI );
cv::imshow( "src", image );
}
}
break;
}
}
cv::Mat notchFilter_BTW(int rows,int cols,std::vector<cv::Point> np,
float* D,int n=1,int cvtype=CV_32FC2)
{
cv::Mat filt(rows,cols,cvtype,cv::Scalar::all(0));
int cx=cols/2,cy=rows/2;
int numNotch=np.size();
float* D2=D;
for(int i=0;i<numNotch;i++)
{
D2[i]=D[i]*D[i];
}
int uk[numNotch],vk[numNotch];//在畫面上的實際陷波座標點
int u_k[numNotch],v_k[numNotch];//陷波共軛點
float Dk[numNotch],D_k[numNotch];//陷波半徑r
float Hk[numNotch],H_k[numNotch];
for(int i=0;i<rows;i++)
{
for(int j=0;j<cols;j++)
{
int u=cx-j,v=cy-i;//中心座標
for(int s=0;s<numNotch;s++)
{
uk[s]=u+np[s].x,vk[s]=v+np[s].y;
Dk[s]=uk[s]*uk[s]+vk[s]*vk[s];//距離中心半徑的平方
Hk[s]=1-1/(1+pow(Dk[s]/D2[s],n));
u_k[s]=u-np[s].x,v_k[s]=v-np[s].y;
D_k[s]=u_k[s]*u_k[s]+v_k[s]*v_k[s];
H_k[s]=1-1/(1+pow(D_k[s]/D2[s],n));
}
//.data返回的是uchar*型指針,所以要強制轉換成浮點數型
float* p=(float*)(filt.data+i*filt.step[0]+j*filt.step[1]);
for(int c=0;c<filt.channels();c++)
{
p[c]=Hk[0]*H_k[0];
for(int k=1;k<numNotch;k++)
{
p[c]*=Hk[k]*H_k[k];
}
}
}
}
return filt;
}
其中,M/2-u_k,N/2- v_k就是計算出來的座標值,不需要用到M/2, N/2。