zoukankan      html  css  js  c++  java
  • 【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做变形能够做到这一点!
  • 相关阅读:
    postgresql小纪
    Java的大内存分页支持
    GCViewer / MAT
    js给数组去重写法
    解决mybatis foreach 错误: Parameter '__frch_item_0' not found
    JSON字符串和JS对象之间的转换
    使用IntelliJ IDEA搭建多maven模块JAVA项目
    jstl中的sql:query标签获取的结果如何格式化输出
    Label控件如何根据字符串自定义大小
    winform/窗体鼠标事件编程中的几个问题
  • 原文地址:https://www.cnblogs.com/guxin/p/csharp-image-distort-tool.html
Copyright © 2011-2022 走看看