當前位置:
首頁 > 知識 > MeanShift濾波演算法與實現

MeanShift濾波演算法與實現

本文將嘗試使用MeanShift濾波來做磨皮演算法;

MeanShift即均值漂移,最早由Fukunage在1975年提出,論文名字為:The Estimation of the Gradient of a density function.

MeanShift一般是指一個迭代的步驟,即先算出當前點的偏移均值,然後以此為新的起始點,繼續移動,直到滿足一定的結束條件;MeanShift廣泛應用於圖像聚類、平滑、分割和跟蹤方面,本文主要講的是圖像的平滑濾波,嘗試應用於人像的磨皮演算法中;

我們使用一張圖來講解MeanShift的演算法原理(此圖來自網路):

MeanShift濾波演算法與實現

Fig.1基本MeanShift演算法示意圖

我們假設起始位置的濾波半徑為Radius,也就是圖a中的藍色圓形區域半徑,圖a為起始位置,假設紅色點為目標像素,每個目標像素包含位置特徵和像素RGB特徵;

1,計算圖a起始位置處,半徑Radius內目標像素的位置特徵和像素RGB特徵的均值M,如圖c所示;

2,將起始位置的初始特徵(位置特徵和RGB特徵)更新為特徵M;

3,計算M處半徑Radius區域內,目標像素的均值特徵M;

4,按照1-3的過程進行迭代,直到滿足一定的迭代次數和限制條件;

5,圖a中起始位置的RGB特徵值即為迭代完成時M的RGB特徵值,如圖f所示;

整個過程也叫均值漂移,實際上不是位置從圖a起始值漂移到了f圖中的位置,而是圖a和圖f處的特徵值歸為了一類,當然這裡指的是RGB像素值;

這裡我們只講最基本的MeanShift平滑濾波演算法,對於改進的MeanShift演算法不做講解;

演算法流程如下:

1,假設當前像素點P(i,j),濾波半徑為R,迭代次數閾值為maxIter,像素差值閾值為threshold;

2,計算以P為中心,R為半徑的圓形區域S內目標像素的均值特徵,包含像素rgb的均值特徵和位置的均值特徵(質心),計算公式如下:

MeanShift濾波演算法與實現

其中K為核函數,這裡取得是|x-y|;

1,將P的特徵值M更新為2中計算的新特徵值;

2,按照2-3的步驟進行迭代,直到滿足迭代次數閾值maxIter停止,P處的像素值即迭代終結時的rgb特徵值;

上述即為MeanShift平滑濾波演算法的流程,該演算法最大缺點為速度慢,本文用它來嘗試磨皮效果,採用YCbCr顏色空間,僅對Y通道處理,以此加速;

效果圖如下所示:

MeanShift濾波演算法與實現

完整C代碼如下:

[cpp] view plain copy

  1. #include "string.h"
  2. #include "stdio.h"
  3. #include "stdlib.h"
  4. #include "math.h"
  5. #include"f_MeanShiftFilter.h"
  6. #include"TRGB2YCbCr.h"
  7. #define MIN2(a, b) ((a) < (b) ? (a) : (b))
  8. #define MAX2(a, b) ((a) > (b) ? (a) : (b))
  9. #define CLIP3(x, a, b) MIN2(MAX2(a,x), b)
  10. int

    MeanShiftOneChannel(unsigned

    char

    * srcData,

    int

    width ,

    int

    height,

    int

    radius,

    int

    threshold,

    int

    maxIter)
  11. {
  12. int

    len =

    sizeof

    (unsigned

    long

    ) * width * height;
  13. int

    i, j;
  14. int

    gray = 0, sum = 0, srcGray = 0, count = 0;
  15. unsigned

    char

    * tempData = (unsigned

    char

    *) malloc(

    sizeof

    (unsigned

    char

    ) * height * width);
  16. memcpy(tempData, srcData,

    sizeof

    (unsigned

    char

    ) * height * width);
  17. for

    (j = 0; j < height; j++ )

  18. {
  19. for

    (i = 0; i < width; i++)
  20. {
  21. len = i + j * width;
  22. int

    nIter = 0, cx = 0, cy = 0, sumx = 0, sumy = 0;
  23. srcGray = tempData[len];
  24. cx = i;
  25. cy = j;
  26. while

    (nIter < maxIter)
  27. {
  28. sum = 0;
  29. sumx = 0;
  30. sumy = 0;
  31. count = 0;
  32. for

    (

    int

    y = cy - radius; y <= cy + radius; y++)
  33. {
  34. for

    (

    int

    x = cx - radius; x <= cx + radius; x++)
  35. {
  36. int

    px = CLIP3(x, 0, width - 1);
  37. int

    py = CLIP3(y, 0, height - 1);
  38. len = px + py * width;
  39. gray = tempData[len];
  40. if

    (abs(gray - srcGray) < threshold)

  41. {
  42. count++;
  43. sum += gray;
  44. sumx += x;
  45. sumy += y;
  46. }
  47. }
  48. }
  49. if

    (count == 0)
  50. break

    ;
  51. srcGray = sum / count;
  52. cx = sumx / count;
  53. cy = sumy / count;
  54. nIter++;
  55. }
  56. srcData[i + j * width] = srcGray;
  57. }
  58. }
  59. free(tempData);
  60. return

    0;
  61. };
  62. void

    f_MeanShiftFilter(unsigned

    char

    * srcData,

    int

    nWidth,

    int

    nHeight,

    int

    nStride,

    int

    radius,

    int

    threshold,

    int

    maxIter)
  63. {
  64. if

    (srcData == NULL)
  65. {
  66. return

    ;
  67. }
  68. if

    (radius == 0 || threshold == 0)
  69. return

    ;
  70. unsigned

    char

    * yData = (unsigned

    char

    *)malloc(

    sizeof

    (unsigned

    char

    ) * nWidth * nHeight);
  71. unsigned

    char

    * cbData = (unsigned

    char

    *)malloc(

    sizeof

    (unsigned

    char

    ) * nWidth * nHeight);
  72. unsigned

    char

    * crData = (unsigned

    char

    *)malloc(

    sizeof

    (unsigned

    char

    ) * nWidth * nHeight);
  73. unsigned

    char

    * pSrc = srcData;
  74. int

    Y, CB, CR;
  75. unsigned

    char

    * pY = yData;

  76. unsigned

    char

    * pCb = cbData;
  77. unsigned

    char

    * pCr = crData;
  78. for

    (

    int

    j = 0; j < nHeight; j++)
  79. {
  80. for

    (

    int

    i = 0; i < nWidth; i++)
  81. {
  82. RGBToYCbCr(pSrc[2],pSrc[1],pSrc[0],&Y,&CB,&CR);
  83. *pY = Y;
  84. *pCb = CB;
  85. *pCr = CR;
  86. pY++;
  87. pCb++;
  88. pCr++;
  89. pSrc += 4;
  90. }
  91. }
  92. MeanShiftOneChannel(yData, nWidth, nHeight, radius, threshold, maxIter);
  93. pSrc = srcData;
  94. pY = yData;
  95. pCb = cbData;
  96. pCr = crData;
  97. int

    R, G, B;
  98. for

    (

    int

    j = 0; j < nHeight; j++)
  99. {
  100. for

    (

    int

    i = 0; i < nWidth; i++)
  101. {
  102. YCbCrToRGB(*pY, *pCb, *pCr, &R, &G, &B);
  103. pSrc[0] = B;
  104. pSrc[1] = G;
  105. pSrc[2] = R;
  106. pY++;
  107. pCb++;
  108. pCr++;
  109. pSrc += 4;
  110. }
  111. }
  112. free(yData);
  113. free(cbData);
  114. free(crData);
  115. }

代碼已經貼出來了,這裡就不給DEMO了,大家可以直接使用代碼進行測試,代碼中YCbCr轉換函數前文博客中有,或者大家自己實現,都可以。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 程序員小新人學習 的精彩文章:

程序員如何快速搭建個性化主頁
基於 Python 自建分散式高並發 RPC 服務

TAG:程序員小新人學習 |