图像处理小tip——中值滤波的多种实现(包括快速中值滤波算法)
文章目錄
- 中值濾波介紹
- opencv自帶的中值濾波函數
- 簡單算法實現中值濾波
- 快速算法實現中值濾波
中值濾波介紹
中值濾波介紹
中值濾波定義:將𝑛(𝑛為奇數)個數據按其值𝑑𝑖進行從大到小或者從小到大排列后得到一個有序序列:𝑑0,𝑑1, … ,𝑑𝑛?1,則𝑑(𝑛/2)稱為中值。例如:有序序列10,11,12,13,14,15,16,17,18的𝑛 = 9,有 [9/2] = 4,則中值為𝑑4,即14。
??根據以上可知,中值濾波就是以當前像素為中心取一個鄰域,用該區域的所有像素灰度值的中值作為該像素濾波后的灰度值。
中值濾波作用
??中值濾波的作用很大程度上是基于均值濾波來講的,我們知道均值濾波是以某一像素為中心,將其對應的某一領域中所有像素值的均值作為這一中心的濾波后的值,均值濾波總會導致圖像的模糊,而采用中值濾波可以很好的解決這個問題,尤其是在面對椒鹽噪聲這種像素點噪聲時,中值濾波總是會有非常好的效果。
opencv自帶的中值濾波函數
??
中值濾波函數
結果如下:
簡單算法實現中值濾波
??下面我們來詳細敘述一下中值濾波的實現算法,首先是簡單中值濾波,采用一般思維,排序取中值;
偽代碼:
??實現的話應該很簡單,有興趣的可以根據上述偽代碼取嘗試實現,重要的是下面將要敘述的快速算法實現中值濾波。
快速算法實現中值濾波
??算法思想來源于A Fast Two-Dimensional Median Filtering Algorithm這篇論文,有興趣的同學可以去看看,當然我不是很推薦,幾十年前寫的論文,算法流程寫的一點都不簡潔,看的老費勁了。找了半天終于找到中文版的介紹,下面圖片摘自https://blog.51cto.com/qianqing13579/1590217這篇博客,但是于我而言習慣于用opencv的數據格式(哈哈通俗點說,離開opencv圖片都讀不了),大佬的代碼沒法直接用,當然也有脫褲子放屁的嫌疑,你看你又離不開opencv又不他內置的函數,沒辦法誰叫老師就喜歡這種重復造輪子的作業呢。[無奈~~]
/* * 實現中值濾波(算法來源:A Fast Two-Dimensional Median Filtering Algorithm) * 環境:opencv4.4 debug x64 * 博客鏈接: * 僅供學習 */#include <iostream> #include "opencv2/core/core.hpp" #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #include <opencv2\imgproc\types_c.h>using namespace std; using namespace cv;void QuickMedianFluter(Mat OriImage, Mat& ResImage, int size); int FindMedian(int H[], int n,int size,int &mNum);int main() {Mat image1 = imread("salt_pepper_Image.jpg", IMREAD_GRAYSCALE);if (image1.empty()){cout << "讀取圖片失敗。" << endl;}imshow("Origin Image", image1);//中值濾波,調用opencv自帶的函數 Mat result1;medianBlur(image1, result1, 3);imshow("Result1 Image(after median fluter,opencv自帶)", result1);//快速中值濾波,利用自己編寫的函數Mat result2;result2 = image1.clone();//ResImage = OriImage.clone();QuickMedianFluter(image1, result2, 3);imshow("Result2 Image(自我編寫函數)", result2);waitKey(0);return 0; }/*********************************************************** * 函數功能:中值濾波(利用直方圖法快速實現) * 參數介紹:OriInage:原圖 ResImage:濾波后的圖 * size:濾波器(或者領域)邊長,大小為size x size(如3x3) ***********************************************************/void QuickMedianFluter(Mat OriImage, Mat& ResImage, int size) {int m = OriImage.rows; //m是行,對應下面的xint n = OriImage.cols; //n是列,對應下面的nint *Histogram = new int[256];memset(Histogram, 0, 256 * sizeof(int));int radius = size / 2; //半徑int th = size * size / 2 + 1; //算法中的thint median = 0; //保存直方圖中的中值對應的像素int mNum = 0; //保存小于中值像素median的總個數int left = 0; //最左列將要移除的int right = 0; //最右邊將要加入的if (OriImage.channels() != 1){cout << "圖片不是灰度圖像,請轉化為灰度圖像后再嘗試。" << endl;}//處理邊界,邊界沒法進行中值濾波,因此將邊界所有像素賦值給ResImage//直接clone過去即可,中間部分會隨著濾波過程的進行隨之改變for (int i = radius; i < m - radius; i++) //m是行,對應x從上至下{//初始化Histogrammemset(Histogram, 0, 256 * sizeof(int));for (int k = i-radius; k <= i+radius; k++){for (int h = 0; h < size; h++){Histogram[OriImage.at<uchar>(k, h)]++;}}median= FindMedian(Histogram, 256, size,mNum);ResImage.at<uchar>(i,radius) = median;for (int j = radius; j < n - radius; j++) //n是列,對應y從左至右{//j在radius處,直方圖已求,跳過if (j == radius) continue; //根據算法,中心向右移動一位,將最左列的數移除直方圖中for (int k = i-radius; k <= i + radius; k++){left = OriImage.at<uchar>(k, j - 1 - radius);Histogram[left]--;if (left <= median) mNum = mNum - 1;}//根據算法,中心向右移動一位,將最右側的數移入直方圖中for (int k = i-radius; k <= i + radius; k++){right = OriImage.at<uchar>(k, j + radius);Histogram[right]++;if (right <= median) mNum = mNum + 1;}if (mNum == th){ResImage.at<uchar>(i, j) = median;continue;}//利用前一中值和所有小于等于中值median的數mNum,求當前中值if (mNum < th){//小于th(第th個數為中值),在當前median右邊,往右找(像素加一尋找)while (mNum < th) {median = median + 1;mNum = mNum + Histogram[median];}}else {//大于th(第th個數為中值),在當前median左邊,往右找(像素減一尋找)while (mNum > th){mNum = mNum - Histogram[median];median = median - 1;}}ResImage.at<uchar>(i, j) = median;}}delete[] Histogram; }/************************************************************** * 函數功能:通過直方圖返回中值 * 參數介紹:H[]:直方圖數組 n:直方圖維數 * size:濾波器的大小 ****************************************************************/int FindMedian(int H[], int n,int size,int &mNum) {int median = 0; //初始化中值為0int sum_cout = 0; //求和int median_flag = size * size / 2; //中值標志for (int i = 0; i < 256; i++){sum_cout += H[i];if (sum_cout > median_flag){median = i;break;}}mNum = sum_cout;return median; }結果:
另外提一句,使用imread()函數讀取圖片時,最好加上IMREAD_GREYSCALE(表示讀取為灰度圖片)等類似限定圖片類型的限定參數,不然可能會出現很多意想不到的意外,我就是因為這個問題,改了兩天的bug。。。淚目。。。
稍微說下吧,當我用
Mat image1 = imread("salt_pepper_Image.jpg");直接讀取時,最終結果是這樣:
很奇怪吧,只有左邊一部分進行了 中值濾波,我查過總共是 150列(利用image.cols),但是實際上遠不是150列。
當我加上IMREAD_GREYSCALE時,即:
Mat image1 = imread("salt_pepper_Image.jpg",IMREAD_GREYSCALE);后,輸出的圖片是這樣的:
所以,不能 偷懶,該寫上的 東西就別忘!!!
總結
以上是生活随笔為你收集整理的图像处理小tip——中值滤波的多种实现(包括快速中值滤波算法)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于matlab的中值滤波算法浅析
- 下一篇: Java商城系统后端和小程序模板、毕业设