我正在为图像旋转编写代码。但表现太糟糕了。
当我使用10000 x 10000图像时,花了大约100分钟。我希望加速到< 1~3分钟,但我不知道如何改进此代码。
此代码适用于大尺寸图像(超过4gb),并按顺序处理单行图像。
我附上完整的代码。首先我进行x-y运动变换,然后进行角度变换。
这是代码。环境是.net框架4.6.2,64位。
测试代码:我使用了" emgucv3和speedycoding"来自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;
}