【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;
            }
        }
        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做變形能夠做到這一點!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM