首頁  >  文章  >  web前端  >  PhotoShop演算法原理解析系列 - 像素化-碎片

PhotoShop演算法原理解析系列 - 像素化-碎片

高洛峰
高洛峰原創
2017-02-21 09:18:171698瀏覽

      接著上一篇文章的熱度,繼續講一些稍微簡單的演算法。

      本文來講講碎片演算法,先貼幾個效果圖:

     PhotoShop算法原理解析系列 - 像素化-碎片  PhotoShop算法原理解析系列 - 像素化-碎片    PhotoShop算法原理解析系列 - 像素化-碎片     PhotoShop算法原理解析系列 - 像素化-碎片

* *

      這是個破壞性的濾鏡,拿美女來說事是因為搞影像的人90%是男人,色色的男人。

      關於碎片濾鏡的原理,網路上可找到的資料為:將影像建立四個相互偏移的副本,產生類似重影的效果。

      就憑上述一句話,我們就可以動手了。

      分析:透過上述幾個影像的比較,特別是眼睛部位,可以看出處理的圖應該看得出像是單眼變成了4個眼睛,因此,網絡上的說法可靠。

      那麼偏移的中心在哪裡,偏移的數量又是多少呢,4個偏移,分別是往那些方向偏移呢,這些問題也很簡單,可以那PS做驗證:

      具體步驟如下:打開一幅影像,在影像顏色較單調的地方(例如上述美女的手臂處)填滿一處2*2像素的紅色,接著複製圖層,將複製後的圖層進行碎片濾鏡處理,並調整圖層透明度為50%,局部放大可取得下列影像:

                 &*PhotoShop算法原理解析系列 - 像素化-碎片

     

 如此效果,則可輕易得出結論:

       偏移的中心就是以每個像素為中心,4個偏移分別以中心對稱,斜45度均勻圓週佈置,水平和垂直偏移各45度,偏移量4個像素。

       那麼如何疊加的問題應該可以猜測,是取四次偏移後累積值的平均值。

       針對如此思路,我寫出如下算法:

private void CmdFragment_Click(object sender, EventArgs e)
{    int X, Y, Z, XX, YY;    int Width, Height, Stride;    int Speed, Index;    int SumR, SumG, SumB;
    Bitmap Bmp = (Bitmap)Pic.Image;    if (Bmp.PixelFormat != PixelFormat.Format24bppRgb) throw new Exception("不支持的图像格式.");

    Width = Bmp.Width; Height = Bmp.Height; Stride = (int)((Bmp.Width * 3 + 3) & 0XFFFFFFFC);    byte[] ImageData = new byte[Stride * Height];                                    // 用于保存图像数据,(处理前后的都为他)
    byte[] ImageDataC = new byte[Stride * Height];                                   // 用于保存克隆的图像数据
    int[] OffsetX = new int[] { 4, -4, -4, 4 };                                      // 每个点的偏移量
    int[] OffsetY = new int[] { -4, -4, 4, 4 };    fixed (byte* P = &ImageData[0], CP = &ImageDataC[0])
    {        byte* DataP = P, DataCP = CP;
        BitmapData BmpData = new BitmapData();
        BmpData.Scan0 = (IntPtr)DataP;                                              //  设置为字节数组的的第一个元素在内存中的地址
        BmpData.Stride = Stride;
        Bmp.LockBits(new Rectangle(0, 0, Bmp.Width, Bmp.Height), ImageLockMode.ReadWrite | ImageLockMode.UserInputBuffer, PixelFormat.Format24bppRgb, BmpData);

        Stopwatch Sw = new Stopwatch();                                             //  只获取计算用时        Sw.Start();
        System.Buffer.BlockCopy(ImageData, 0, ImageDataC, 0, Stride * Height);     //  填充克隆数据        

        for (Y = 0; Y < Height; Y++)
        {
            Speed = Y * Stride;            for (X = 0; X < Width; X++)
            {
                SumB = 0; SumG = 0; SumR = 0;                for (Z = 0; Z < 4; Z++)                                           //  累积取样点的取样和                {
                    XX = X + OffsetX[Z];
                    YY = Y + OffsetY[Z];                    if (XX < 0)                                                    //   注意越界
                        XX = 0;                    else if (XX >= Width)
                        XX = Width - 1;                    if (YY < 0)
                        YY = 0;                    else if (YY >= Height)
                        YY = Height - 1;
                    Index = YY * Stride + XX * 3;
                    SumB += DataCP[Index];
                    SumG += DataCP[Index + 1];
                    SumR += DataCP[Index + 2];
                }

                DataP[Speed] = (byte)((SumB+2) >> 2);    //  求平均值(Sum+2)/4,为什么要+2,就为了四舍五入。比如如果计算结果为108.6,则取像素109更为合理     
                DataP[Speed + 1] = (byte)((SumG + 2) >> 2);
                DataP[Speed + 2] = (byte)((SumR + 2) >> 2);
                Speed += 3;                                                     //  跳往下一个像素            }
        }
        Sw.Stop();        this.Text = "计算用时: " + Sw.ElapsedMilliseconds.ToString() + " ms";
        Bmp.UnlockBits(BmpData);                         //  必须先解锁,否则Invalidate失败     }
    Pic.Invalidate();}

 

 演算法中, OffsetX 和 OffsetY分別為取樣點像素的偏移量。同樣,由於該濾鏡涉及了領域操作,在處理前需要做像素備份,但這裡沒有對備份資料進行擴展。因此,在內部程式碼裡就需要對取樣點的座標進行驗證,看是否超過其範圍,如果超過範圍,通常在影像濾鏡演算法範圍內,有3種處理方式:

(1)超過了則認為是其最接近的邊界值,即重複邊緣像素,這部分代碼即上述貼出的if ..... else if 部分。

(2)折回,可用以下程式碼來描述:

while (XX >= Width)
    XX = XX - Width;while (XX < 0)
    XX = XX + Width;while (YY >= Height)
    YY = YY - Height;while (YY < 0)
    YY = YY + Height;

 (3 ) 只計算在影像範圍內的像素: 

 if (XX >= 0 && XX < Width && YY >= 0 && YY < Height)
 {
       // 累加计算
 }

     

當然這樣做,就必須用一個變數記錄下都做了多少次符合條件的計算。

      有興趣的朋友可以自己改改程式碼試試看。

      上述程式碼段中DataP[Speed] = (byte)((SumB+2) >> 2);要對SumB加2的原因是為了讓結果進行四捨五入的操作,這樣才較為合理。    

      經過測試,上述程式碼和PS處理的效果100%的吻合。說明我們的猜測是完全正確的。

      還能進一步延伸演算法:  想的遠一點,為什麼非的是4個重影呢,非得是45度角度呢,非得是4個像素的水平和垂直偏移呢。我給下圖讓有興趣的讀者自己研發吧。

     PhotoShop算法原理解析系列 - 像素化-碎片

     圖中,角度為32度,半徑為10,碎片數為7,可產生類似下方的效果(可用我的Imageshop進行驗證):

     PhotoShop算法原理解析系列 - 像素化-碎片       PhotoShop算法原理解析系列 - 像素化-碎片

 更多PhotoShop演算法原理解析系列- 像素化-碎片相關文章請關注PHP中文網!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn