【C#】圖像的變形/變換/扭曲。用Emgu或YLScsFreeTransform(FreeImageTransformation)或MagickImage

需求:將圖像變形,如矩形圖片變換成梯形的,圖素拉伸。

解決方案:目前找到有兩種。

  • 使用EmguCV,它是.Net版的OpenCV。推薦直接在VS裏的Nuget中搜索EmguCV進行下載。
  • 使用第三方庫FreeImageTransformation。(網上搜YLScsFreeTransform關鍵字)
  • 使用第三方庫MagickImage。(非常厲害的魔法~)

思路:首先一張圖片有四個點,給圖片實體類準備一個屬性,用於記錄變形前和變形後的四個點XY座標值。

public float[] ProjectTransform { get; set; }

該數組有16個float型元素,按順序辨別表示:

變形前左上角X值  變形前左上角Y值 變形後左上角X值 變形後左上角Y值
變形前右上角X值  變形前右上角Y值 變形後右上角X值 變形後右上角Y值
變形前左下角X值  變形前左下角Y值 變形後左下角X值 變形後左下角Y值
變形前右下角X值  變形前右下角Y值 變形後右下角X值 變形後右下角Y值

若使用方案一:EmguCV

  • 將Emgu的dll引入到項目中。
    這裏寫圖片描述
  • 實體類中定義一個Image屬性用於保存圖像,該屬性是UMat類型。這是OpenCV中的類型。
  • 使用圖像變形函數 CvInvoke.WarpPerspective()
private UMat image;
public UMat Image
{
    get
    {
        if (ProjectTransform != null)
        {
             PointF[] corner = new PointF[4];
             PointF[] trans_corner = new PointF[4];
             UMat trans_img = new UMat();

             // 變換前的四個角的座標
             corner[0] = new PointF(ProjectTransform[0], ProjectTransform[1]);
             corner[1] = new PointF(ProjectTransform[4], ProjectTransform[5]);
             corner[2] = new PointF(ProjectTransform[8], ProjectTransform[9]);
             corner[3] = new PointF(ProjectTransform[12], ProjectTransform[13]);

             // 變換後的四個角的座標
             trans_corner[0] = new PointF(ProjectTransform[2], ProjectTransform[3]);
             trans_corner[1] = new PointF(ProjectTransform[6], ProjectTransform[7]);
             trans_corner[2] = new PointF(ProjectTransform[10], ProjectTransform[11]);
             trans_corner[3] = new PointF(ProjectTransform[14], ProjectTransform[15]);

             // 變形規則
             Mat transform = CvInvoke.GetPerspectiveTransform(corner, trans_corner);
             // 圖像變形
             CvInvoke.WarpPerspective(image, trans_img, transform, new Size(image.Cols, trans_img.Rows));
             return trans_img;
         }

         return image; // 如果該圖片沒有ProjectTransform屬性,說明該圖不需要變形,直接取出來使用即可。
     }
     set { image = value; }
}

若使用方案二:第三方庫YLScsFreeTransform

  • 從下載的項目中,通過閱讀源碼,只取出我們需要用到的部分,做成工具類。(下載地址
    這裏寫圖片描述
  • 使用辦法如下:
private Bitmap image
public Bitmap Image
{
    get
    {
        if (ProjectTransform != null)
        {
            PointF[] trans_corner = new PointF[4];

            // 扭曲圖像
            trans_corner[0] = new PointF(ProjectTransform[2], ProjectTransform[3]); // 變換後的左上角XY
            trans_corner[1] = new PointF(ProjectTransform[6], ProjectTransform[7]); // 變換後的右上角XY
            trans_corner[2] = new PointF(ProjectTransform[10], ProjectTransform[11]); // 變換後的左下角XY
            trans_corner[3] = new PointF(ProjectTransform[14], ProjectTransform[15]); // 變換後的右下角XY

            using (System.Drawing.Bitmap sourceImg = image.Bitmap)
            {
                YLScsDrawing.Imaging.Filters.FreeTransform filter = new YLScsDrawing.Imaging.Filters.FreeTransform();
                filter.Bitmap = sourceImg;
                // assign FourCorners (the four X/Y coords) of the new perspective shape
                //filter.FourCorners = new System.Drawing.PointF[] { trans_corner[0], trans_corner[1], trans_corner[2], trans_corner[3] };
                filter.VertexLeftTop = trans_corner[0];
                filter.VertexTopRight = trans_corner[1];
                filter.VertexBottomLeft = trans_corner[2];
                filter.VertexRightBottom = trans_corner[3];

                filter.IsBilinearInterpolation = true; // optional for higher quality
                System.Drawing.Bitmap perspectiveImg = filter.Bitmap;

                return perspectiveImg;
            }
            #endregion
        }
        return image;          
    }
    set { image = value; }
}

若使用方案三:第三方庫MagickImage

  • 在NuGet中搜MagickImage,下載最高下載量那個,導入項目中。
    這裏寫圖片描述
public Bitmap Image
{
    get
    {
        return image;          
    }
    set
    {
        if (ProjectTransform != null)
        {
            using (ImageMagick.MagickImage magickImage = new ImageMagick.MagickImage(value))
            {
                magickImage.VirtualPixelMethod = ImageMagick.VirtualPixelMethod.Transparent;
                magickImage.MatteColor = new ImageMagick.MagickColor(255, 255, 255, 0);
                magickImage.FilterType = ImageMagick.FilterType.Point;
                magickImage.Distort(ImageMagick.DistortMethod.Perspective, ProjectTransform);

                image = magickImage.ToBitmap();
                image.SetResolution(72, 72); // 坑點:因爲WPF的默認DPI爲96的問題,在圖像轉型Bitmap時DPI會改變,需要手動修改。
            }
        }
        else
        {
            image = value;
        }
    }
}
  • 小問題:注意這次圖片變形的過程寫在了Set()中而不是Get()中,因爲寫Get中運行時偶爾會發生圖片未經過變形處理的情況,猜想可能是因爲WPF的綁定時機及先後順序問題,寫到Set中因爲能確保For循環是按順序執行的。

小結:

  • 在項目中,由於有幾十張2400*1440的大圖需要扭曲變形(項目需求是用2D圖片的扭曲拉伸來模擬3D透視效果)。在切換圖素/重新加載場景時,使用EmguCV會導致程序直接崩潰(EmguCV報錯),可能是因爲圖像沒有釋放乾淨導致內存爆炸。所以最後改用了C#原生的YLScsFreeTransform庫。運行效率感覺比OpenCV的更快。。。。最重要的是,更多的圖片變形也沒有導致程序崩潰。
  • 2017.5.24更新:最後選用了方案三MagickImage,是因爲用YLScsFreeTransform變形的圖片時等比縮放的,因此2D圖像從矩形變形爲梯形來模擬3D效果時,無法做到近大遠小的透視效果。而用MagickImage做變形能夠做到這一點!
發佈了127 篇原創文章 · 獲贊 53 · 訪問量 83萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章