影象美容之眼睛放大演算法。

Imageshop發表於2014-07-15

     目前,手機上各種影象特效的軟體應用App越來越盛行,比較有名如美圖秀秀,camare360,美顏相機等,還有一些在某些特定的方向做的比較的優秀的如魔漫相機等。這些軟體幾乎無一例外的都提供了相當數量的針對人臉進行美化的功能,正是這些最基礎的功能吸熱了很多熱血美女丑女中女的興趣。以至於幾乎我所認識的每個25-35之間的女性朋友都知道美圖秀秀,而瞭解Photoshop的則微乎其微。不過最近聽一些朋友談到,認為手機上的影象軟體已經過了開發的鼎盛期,也不曉得到底是不是這樣。

    作為人臉中最有神的部位眼睛,自然是各軟體開發商不會錯過的美化物件,拿執行速度極差的可牛影像為例,他至少提供了眼睛放大、去紅眼、祛黑眼圈、眼睛變色、加眼影等眾多和眼睛有關的美容刀。其實這些,在程式開發者開來不過一些美妙的程式碼發言後綻放多多鮮花,可確是眾多人為之用之美容的神器。

    但是,這些簡單的功能,送之於百度或這個google搜尋相關演算法,能找到想匹配的資訊量真的不多,最起碼我沒找到可以直接用之於實踐的東西。

    本文就共享一些我關於眼睛縮放這個問題的一些研究成果,這些研究是3年前進行的。

    正規的來說,眼睛縮放屬於影象的一種區域性扭曲,關於扭曲,Photoshop中的液化濾鏡最能體現這類演算法的靈魂。眼睛縮放完全可以用其中的一種方式來實現,如下圖所示:

                   (為節省篇幅,旋轉了下)

    使用該膨脹工具,選擇合適的引數能得到非常理想的效果,但是如果僅僅為了這個功能區研究龐大的液化濾鏡的演算法,是極其需要勇氣的精力的。雖然目前在開源內的軟體中可以從GIMP的Iwarp程式碼或者paint.net的一個smudgle外掛通過反編譯的方式得到程式碼參考學習,但是這個過程是比較痛苦的,因為我因為痛苦過一次了,這裡分享一個簡單的方式去實現他,就減輕大家的痛苦了吧。

    在手動的眼睛縮放過程中,一般有三個引數:中心點、畫筆大小、力度,我們分別用(PointX,PointY),Radius,Strength表示。比如美圖提供瞭如下的使用者介面:

                   

  中心點是使用者通過滑鼠單擊獲得的,畫筆的大小決定了演算法所影響到的範圍。力度影響眼睛放大的程度。

  我的演算法是通過如下圖所示的簡單過程實現的。

             

  對於畫筆半徑以內的任一位置畫素(X,Y),按照其所在的位置和半徑之間的比值,根據強度的設定值按照某個線性的公式,從經過點(PointX,PointY)和(X,Y)的直線中選擇一個位置畫素作為新的畫素值,如果這個新的位置位於兩點之間(圖中X1,Y1),則出現眼睛縮小的效果,而位於直線的延長線上(圖中的X2,Y2),則會有眼睛放大的效果,用一段簡單的程式碼表示就是:

 

    Left = IIf(PointX - Radius < 0, 0, PointX - Radius)                     '  計算邊界值
    Top = IIf(PointY - Radius < 0, 0, PointY - Radius)
    Right = IIf(PointX + Radius >= m_Width, m_Width - 1, PointX + Radius)
    Bottom = IIf(PointY + Radius >= m_Height, m_Height - 1, PointY + Radius)
    PowRadius = Radius * Radius

    For Y = Top To Bottom
        OffSetY = Y - PointY
        For X = Left To Right
            OffsetX = X - PointX
            XY = OffsetX * OffsetX + OffSetY * OffSetY                      '   距離的平方
            If XY <= PowRadius Then                                         '   在指定的半徑內
                ScaleFactor = 1 - XY / PowRadius
                ScaleFactor = 1 - Strength / 100 * ScaleFactor              '   按照這種關係計算取樣點的位置
                PosX = OffsetX * ScaleFactor + PointX
                PosY = OffSetY * ScaleFactor + PointY
                If PosX < 0 Then                                            '   放置越界
                    PosX = 0
                ElseIf PosX >= m_Width Then
                    PosX = m_Width - 1
                End If
                If PosY < 0 Then
                    PosY = 0
                ElseIf PosY >= m_Height Then
                    PosY = m_Height - 1
                End If
                Speed = Y * m_Stride + X * 3
                Index = PosY * m_Stride + PosX * 3
                ImageData(Speed) = ImageDataB(Index)                        '將當前點的值賦值為取樣點的值
                ImageData(Speed + 1) = ImageDataB(Index + 1)
                ImageData(Speed + 2) = ImageDataB(Index + 2)
            End If
        Next
    Next

 

  我沒有提供的完整的程式碼,是因為真正懂的人只要從隻言片語就能搞清楚,而那些伸手黨在我這裡是不收歡迎的。

  上述程式碼有很多可以改進的地方,第一,上述出現的結果會有些不清晰,只是 由於計算的取樣的座標實際上是浮點數,因此直接取整會帶來較大的誤差,較為合理的做法是利用雙線性插值之類的插值演算法進行計算優化。第二:線性對映的計算公式也可以做調整,比如調整成那種離眼珠越進的那些畫素的變化越小,而越遠的變換越大等。

       效果如下圖:

                                   

      按照上述細路寫了個測試程式:http://files.cnblogs.com/Imageshop/ZoomEyes.rar

    

 

   

   

****************************作者: laviewpbt   時間: 2014.7.15    聯絡QQ:  33184777 轉載請保留本行資訊**********************

 

相關文章