图像旋转性能改善

时间:2018-04-11 01:25:36

标签: c# performance image-processing

我正在为图像旋转编写代码。但表现太糟糕了。

当我使用10000 x 10000图像时,花了大约100分钟。我希望加速到< 1~3分钟,但我不知道如何改进此代码。

此代码适用于大尺寸图像(超过4gb),并按顺序处理单行图像。

我附上完整的代码。首先我进行x-y运动变换,然后进行角度变换。

这是代码。环境是.net框架4.6.2,64位。

测试代码:我使用了&#34; emgucv3和speedycoding&#34;来自nuget包的库 flatten方法来自speedycoding,而Image<Gray, byte>来自emgucv3

Image<Gray,byte> img = new Image<Gray, byte>(imgpath);

PointD[] posdList = new PointD[4]
                    {
                        new PointD(-1024,48),
                        new PointD(-3264,0),
                        new PointD(639,-2016),
                        new PointD(3119,1696)
                    };

var data = img.Data.TointArray();
var dataW = img.Data.GetLength(1);
var dataH = img.Data.GetLength(0);

var strideW = dataW % 4;
var strideH = dataH % 4;

posdList = posdList.Select( x => new PointD( x.X * ratio + dataW/2, x.Y * ratio+dataH/2 ) ).ToArray();
img = img.Resize( dataW + ( strideW == 0 ? 0 : 4 - strideW ), dataH + ( strideH == 0 ? 0 : 4 - strideH ), Inter.Cubic );

data = img.Data.TointArray();
dataW = img.Data.GetLength(1);
dataH = img.Data.GetLength(0);

var srcdata = img.Data.Flatten();
byte[][] temprers;

using (MemoryStream mstream = new MemoryStream(srcdata))
{
    temprers = Run(mstream, dataW, dataH, posdList);
}

这是主要的运行代码。

public static byte[][] Run(MemoryStream bytestream, int w, int h, PointD[] pos3)
    {
        try
        {
            Stopwatch stw = new Stopwatch();
            var srcPos = new AffinePos(pos3[0], pos3[1], pos3[2], pos3[3]);
            var trsData = srcPos.ToTrnsData(w, h);

            var xc = trsData.XSrcCnter;
            var yc = trsData.YSrcCnter;

            var xmax = Math.Max(w - xc, xc) * 2;
            var ymax = Math.Max(h - yc, yc) * 2;

            byte[][] R1 = CreateJagged(ymax, xmax);

            for (int i = 0; i < h; i++)
            {
                var data = new byte[w];
                bytestream.Seek(i * w, SeekOrigin.Begin);
                bytestream.Read(data, 0, w);

                var reshaped = data.Reshape(1, w).ToJagged();
                xytransform(ref R1, trsData, reshaped, i, xmax, ymax);
            }

            var w1 = R1[0].Length;
            var h1 = R1.Length;

            var degree = trsData.Angle * 180 / Math.PI;

            double radian = trsData.Angle;
            double cosRadian = Cos(radian);
            double sinRadian = Sin(radian);
            int newWidth = (int)(w1 * Abs(cosRadian) + h1 * Abs(sinRadian));
            int newHeight = (int)(h1 * Abs(cosRadian) + w1 * Abs(sinRadian));
            Console.WriteLine( "Create Jagged (sec)" );
            stw.Start();
            byte[][] R2 = CreateJagged(newWidth, newHeight);
            stw.Stop();

            Console.WriteLine( stw.ElapsedMilliseconds / 1000 );


            Console.WriteLine( "Length : {0} / (ms)", R1.Length );
            for (int i = 0; i < R1.Length; i++)
            {
                var reshaped = R1[i].Reshape(1, w1).ToJagged();
                Stopwatch stwinnter = new Stopwatch();
                stwinnter.Start();
                rotateGeneral(ref R2, trsData, reshaped, i, w1, h1);
                stwinnter.Stop();
                Console.WriteLine( stwinnter.ElapsedMilliseconds  );
            }
            return R2;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
            return null;
        }

    }


public static void xytransform(ref byte[][] target, TrnsData data, byte[][] src, int hidx, int xmax, int ymax)
    {

        var h = src.GetLength(0);
        var w = src[0].GetLength(0);

        var tcenterX = (int)xmax / 2;
        var tcenterY = (int)ymax / 2;

        var xshift = (int)(data.XSrcCnter - tcenterX);
        var yshift = (int)(data.YSrcCnter - tcenterY);



        for (int j = 0; j < h; j++)
        {
            for (int i = 0; i < w; i++)
            {
                if (i - xshift >= 0
                    && j - yshift >= 0
                    && i - xshift < target[0].Length
                    && j - yshift < target.Length)
                {
                    var yidx = (j - yshift + hidx).ToString();
                    var xidx = (i - xshift).ToString();


                    if (j - yshift + hidx * h < target.Length
                        && i - xshift < target[0].Length)
                    { target[j - yshift + hidx * h][i - xshift] = src[j][i]; }
                }
            }
        }
    }
        public static void rotateGeneral(ref byte[][] target, TrnsData data, byte[][] G, int hidx, int oldw, int oldh)
    {

        double radian = data.Angle;
        var newWidth = target[0].Length;
        var newHeight = target.Length;

        double cosRadian = Cos(radian);
        double sinRadian = Sin(radian);


        int centerX = oldw / 2;
        int centerY = oldh / 2;
        int diffX = (newWidth - oldw) / 2;
        int diffY = (newHeight - oldh) / 2;


        double sourceX, sourceY;
        int isourceX, isourceY;
        int isourceX2, isourceY2;

        int h = G.Length;
        var temptarget = target;

        Parallel.For( 0, target.Length ,
            y =>
            {
                for (int x = 0; x < temptarget[0].Length; x++)
                {
                    var dx = x - (centerX + diffX);
                    var dy = y - (centerY + diffY);

                    var xres = dx * cosRadian - dy * sinRadian;
                    var yres  = dx * sinRadian + dy * cosRadian;

                    var srcX = xres + centerX;
                    var srcY = yres - hidx * h + centerY;


                    var isourceX_ = (int)Math.Round( srcX );
                    var isourceY_ = (int)Math.Round( srcY );

                    //try
                    //{
                        if (isourceY_ < G.Length
                            && isourceY_ >= 0
                            && isourceX_ < G[0].Length
                            && isourceX_ >= 0)

                        {
                            temptarget[y][x] = G[isourceY_][isourceX_];
                        }
                    //}
                    //catch (Exception es)
                    //{
                    //  Console.WriteLine( es.ToString() );
                    //}

                }

            } );

        target = temptarget;
    }

transData类就是这个

public struct TrnsData
{
    public double Innerw;
    public double Innterh;

    public int H;
    public int W;
    public int XSrcCnter;
    public int YSrcCnter;
    public int XShift;
    public int YShift;
    public int dX;
    public int dY;
    public double Angle; // radian
}

   public class AffinePos
{
    public PointD LB;
    public PointD LT;
    public PointD RT;
    public PointD RB;

    public AffinePos(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
    {
        LB = new PointD(x1, y1);
        LT = new PointD(x2, y2);
        RT = new PointD(x3, y3);
        RB = new PointD(x4, y4);
    }

    public AffinePos(PointD lb, PointD lt, PointD rt, PointD rb)
    {
        LB = lb;
        LT = lt;
        RT = rt;
        RB = rb;
    }
}

这是创建TrnsData的扩展方法

        public static TrnsData ToTrnsData
      (this AffinePos srcPos, int w, int h)
    {
        var innerh = PosL2(srcPos.LB, srcPos.LT);
        var innerw = PosL2(srcPos.RT, srcPos.LT);


        var trgPos = srcPos.MoveToCenter(new PointD(w / 2.0, h / 2.0)); // ok

        var fcenterX = srcPos.GetCenter().X;
        var fcenterY = srcPos.GetCenter().Y;
        var lcenterX = trgPos.GetCenter().X;
        var lcenterY = trgPos.GetCenter().Y;

        var xshift = lcenterX - fcenterX;
        var yshift = lcenterY - fcenterY;

        var dx = srcPos.LT.X - srcPos.RT.X;
        var dy = srcPos.LT.Y - srcPos.RT.Y;
        double radian;
        if (Abs( dx) < 0.0001)
        {
            radian = 1.5708;
        }
        else if (Abs(dy) < 0.0001)
        {
            radian = 3.14159;
        }
        else
        {
            radian = Math.Atan(dy / dx);
        }
        return new TrnsData()
        {
            H = h,
            W = w,
            XShift = (int)xshift,
            YShift = (int)yshift,
            Innerw = innerw,
            Innterh = innerh,
            XSrcCnter = (int)fcenterX,
            YSrcCnter = (int)fcenterY,
            dX = (int)dx,
            dY = (int)dy,
            Angle = radian,
        };
    }

和其他扩展方法

 public static byte[][] CreateJagged(int h, int w)
    {
        byte[][] output = new byte[h][];
        for (int i = 0; i < h; i++)
        {
            output[i] = new byte[w];
        }
        return output;

    }

0 个答案:

没有答案