前面的宣告: 我知道我可以使用OpenCV對影像進行平滑/模糊處理,但我的任務更多的是在引擎蓋下,以便我能夠理解這個程序。我對影像處理和DFT是個新手,所以這被證明是一個挑戰。
描述:
我正在使用FFTW庫和OpenCV進行一些影像處理,將影像讀入OpenCV::Mat物件和grayscale色彩空間。我將資料轉換為double型別,這樣我就可以創建一個雙倍指標,這是FFTW的FFT函式所要求的。這是一個名為imageDouble的Mat物件,它的指標是pImageDouble。
std::string filename = "<path_to_your_image>"/span>。
Mat image = imread(filename, IMREAD_GRAYSCALE)。
/***********************************************************************
***創建*doube*型別的Mat物件,并將影像內容復制到該物件中。
***然后創建一個指向它的指標。
***********************************************************************/
Mat imageDouble。
image.convertTo(imageDouble, CV_64FC1)。
double* pImageDouble = imageDouble.ptr<double>(0)。
我也有一個高斯核,我把它讀到一個Mat物件中。我執行了一個圓形移位,其中高斯核的中心(和最高)值被移到核的左上方((0,0)位置),然后我把核調零到我讀入的原始影像的大小。結果(型別double)被存盤在一個名為paddedGKernel的Mat物件中,指標被命名為pPaddedGKernel。
Mat paddedGKernel = padGKernelWithZeros(nRows, nCols, rolledGKernel)。
double* pPaddedGKernel = paddedGKernel.ptr<double>(0)。
我初始化fftw_complex物件,將FFT結果輸出到fftw_plan中,然后我為ftw_complex物件分配記憶體并執行該計劃。我使用FFTW的fftw_plan_dft_r2c_2d()函式對兩個2D物件(image和shifted/padded Gaussian kernel)進行FFT,然后在Fourier空間進行點乘,將高斯濾波器應用于原始影像。
fftw_complex* out; //for result of FFT for original image。
fftw_complex* outGaussian; //for result of FFT for Gaussian kernel。
fftw_plan p;
/*************************************************
***為fftw_complex物件分配記憶體
*************************************************/
out = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * nRows * nCols) 。
outGaussian = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * nRows * nCols)。
/****************************************
***對imageDouble進行FFT,輸出到out
****************************************/
p = fftw_plan_dft_r2c_2d(imageDouble.rows, imageDouble.cols, pImageDouble, out, FFTW_ESTIMATE)。
fftw_execute(p)。
/**************************************************
***對paddedGKernel的FFT,輸出到outGuassian。
**************************************************/
p = fftw_plan_dft_r2c_2d(paddedGKernel.rows, paddedGKernel.cols, pPaddedGKernel, outGaussian, FFTW_ESTIMATE) 。
fftw_execute(p)。
/****************************************************
***點乘法,應用高斯內核
****************************************************/
for (size_t i = 0; i < nCCols * nRows; i )
{
out[i][0] = out[i][0] * (1 / outGaussian[i][0] )。)
}
然后我對點乘的結果進行逆FFT fftw_plan_dft_c2r_2d(),并將其輸出到OpenCV::Mat物件imageCloneDouble,并將資料轉換回uchar,將資料放入OpenCV::Mat imageBack。
/***********************************************************************
*** 制作Mat物件(型別為*double*),將資料放入其中,并將指標指向它
***********************************************************************/
Mat imageCloneDouble = Mat::zeros(nRows, nCols, CV_64FC1)。
double* pImageCloneDouble = imageCloneDouble.ptr<double>(0)。
/*****************************************************************************
*** 制作并執行反FFT的計劃,輸出到imageCloneDouble。
*** 然后進行歸一化處理,因為這個函式會產生非歸一化的值。
*****************************************************************************/
fftw_plan pp = fftw_plan_dft_c2r_2d(imageCloneDouble.rows, imageCloneDouble.cols, out, pImageCloneDouble, FFTW_BACKWARD | FFTW_ESTIMATE) 。
fftw_execute(pp)。
imageCloneDouble = imageCloneDouble / (nCols * nRows *2) 。
/***************************************************************************
*** 制作Mat物件(型別為*uchar*),并將資料反FFT資料復制到其中
***************************************************************************/
Mat imageBack;
imageCloneDouble.convertTo(imageBack, CV_8UC1)。
當我顯示轉換后的影像imageBack時,我希望得到應用了高斯核的原始影像,但它看起來像是可能有一些修改的原始影像疊加了一個似乎有一些修改的旋轉版本。我不明白這種翻轉/旋轉是在哪里發生的,為什么會疊加在看起來是原始影像的地方,但我懷疑它是在我在傅里葉空間進行點乘或元素乘時發生的。要么就是我遺漏了一個我需要對點乘結果執行的程序。
我需要對傅里葉空間中的這些資料做什么才能回到原始影像?
uj5u.com熱心網友回復:
這是你的問題:
我執行了一個回圈移位,高斯核的中心(和最高)值被移到核的左上角((0,0)位置),然后我把核調零到我讀進去的原始影像的大小。
你需要把這兩個操作倒過來:先墊到合適的尺寸,然后應用回圈移位。你確實需要內核的中心在(0,0)。但你需要內核在圓周率世界中是連續的(即復制影像,你應該能看到完整的高斯內核)。當你在圓周率移位后進行填充時,你在這個圓周率世界中分離了內核的四個象限。
第二個問題是在這一行:
out[i][0] = out[i][0] * (1 / outGaussian[i][0]);
首先,為什么是除法而不是乘法?你是想應用卷積,不是嗎?
第二,你是想應用一個卷積。
第二,你只是對復數的實數分量進行了乘法,而對虛數分量未作任何改動。你需要對兩個復數做一個完整的復數乘法來產生一個新的復數。
std::size_t N = nRows * nCols。
fftw_complex* out = fftw_alloc_complex(N)。
fftw_complex* outGaussian = fftw_alloc_complex(N)。
// ...
auto* out_p = reinterpret_cast<std::complex<double>*>(out)。
auto* outGaussian_p = reinterpret_cast<std::complex<double>*>(outGaussian)。
for (std::size_t i = 0; i < N; i )
{
out_p[i] *= outGaussian_p[i]。
}
注意,std::complex<double>和fftw_complex具有相同的記憶體布局,由std::complex的C 規范保證,目的是能夠進行這種型別的轉換。實際上沒有做任何重新解釋,只是C 型別系統這樣認為。
uj5u.com熱心網友回復:
解決這個問題的關鍵是從Cris指出的第二個問題開始的。所以首先要做的是實作回復中那部分提供的代碼。
auto* out_p = reinterpret_cast<std::complex<double>*> (out)。
auto* outGaussian_p = reinterpret_cast<std::complex<double>*>(outGaussian)。
for (std::size_t i = 0; i < N; i )
{
out_p[i] *= outGaussian_p[i]。
}
我以通常的方式對高斯核進行了規范化處理(除以所有數值的總和)。在對Cris進行上述修改后,影像應用了模糊,唯一的問題是像素值的范圍發生了變化。
為了解決這個問題,我應用了對比度拉伸的公式來匹配原來的范圍,這就解決了問題。
/* 尋找原始影像的最小值和最大值 */。
double oMin, oMax;
minMaxLoc(image, & oMin, & oMax);
/* 為我們的'imageBack'找到最小和最大的值并進行四舍五入。*/
double min, max;
minMaxLoc(imageCloneDouble, &min, &max)。
min = round(min)。
max = round(max)。
/* Code version of formula for contrast stretching */
if (oMin != min | oMax != max) {
double numerator = oMax - oMin;
double denominator = max - min;
double scaledMin = -(min)*numerator;
double scaledOMin = oMin * denominator;
double innerTerm = scaledMin scaledOMin;
imageCloneDouble = (( numerator * imageCloneDouble) innerTerm ) / denominator;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/320329.html
標籤:
上一篇:如何剪掉透明背景?
