应用
我正在分析在红外摄像机上成像的激光束。目标是进行实时测量,或者至少与相机一样快,每125毫秒刷新一次。
算法将对黄色虚线和它们之外的强度求和,然后找出比率。黄色虚线以与光束角度相同的角度旋转。光束的角度几乎不会是45度的倍数。
我从相机接收2D阵列形式的光束。它由一个大小为1360 x 1024的32位整数数组表示。我想旋转数组,使黄色虚线垂直于数组的边界,这将使求和算法更简单。
我尝试了什么:
我调查System.Drawing.Drawing2D.Matrix.Rotate()
,正如在类似问题的答案中所建议的那样。但是,这适用于Graphics
个对象。使用该方法我旋转了图像。但我还没有找到在2D阵列上执行类似操作的方法。
修改
该算法实际上找到w
,使得线条包含光束能量的n%
。我已经尝试遍历整个数组而没有旋转并使用线条检查边界(y = mx + b),但是,由于我对此操作进行了多次迭代,因此成本太高。同样,我在整个图像中有140万个值。
我想要旋转的原因是计算旋转阵列的每一行的总和(产生光束轮廓),然后找到产生w
封闭能量的最窄n%
。
下图显示了旋转光束,轮廓(1D数组的总和,红色)和宽度(蓝色)。
答案 0 :(得分:2)
我有一个有趣的想法,可能与你上面的有点不同。这是算法:
betweenSum
和outsideSum
y = mx+b
)
这种方法有点不同,我不确定这是否适用于您的项目。提供一种方法可以帮助避免轮换(也许任何额外的开销)。
答案 1 :(得分:1)
我认为我从像素到物理坐标的转换是正确的(并且返回)。那你想要的是这个:
public struct ImageData
{
/// <summary>
/// Intensity map
/// </summary>
int[,] intensities;
/// <summary>
/// Pixel dimensios of image like 1360 × 1024
/// </summary>
Size pixel_size;
/// <summary>
/// Physical dimensions like 300μ × 260μ
/// </summary>
SizeF actual_size;
/// <summary>
/// Transforms pixel coordinates to actual dimensions. Assumes center of image is (0,0)
/// </summary>
/// <param name="pixel">The pixel coordinates (integer i,j)</param>
/// <rereturns>The physical coordinates (float x,y)</rereturns>
public PointF PixelToPoint(Point pixel)
{
return new PointF(
actual_size.Width*((float)pixel.X/(pixel_size.Width-1)-0.5f),
actual_size.Height*((float)pixel.Y/(pixel_size.Height-1)-0.5f));
}
/// <summary>
/// Transforms actual dimensions to pixels. Assumes center of image is (0,0)
/// </summary>
/// <param name="point">The point coordines (float x,y)</param>
/// <returns>The pixel coordinates (integer i,j)</returns>
public Point PointToPixel(PointF point)
{
return new Point(
(int)((pixel_size.Width-1)*(point.X/actual_size.Width+0.5f)),
(int)((pixel_size.Height-1)*(point.Y/actual_size.Height+0.5f)));
}
/// <summary>
/// Get the ratio of intensities included inside the strip (yellow lines).
/// Assume beam is located at the center.
/// </summary>
/// <param name="strip_width">The strip width in physical dimensions</param>
/// <param name="strip_angle">The strip angle in degrees</param>
/// <returns></returns>
public float GetRatio(float strip_width, float strip_angle)
{
// Convert degrees to radians
strip_angle*=(float)(Math.PI/180);
// Cache sin() and cos()
float cos=(float)Math.Cos(strip_angle), sin=(float)Math.Sin(strip_angle);
//line through (xc,yc) with angle ψ is (y-yc)*COS(ψ)-(x-xc)*SIN(ψ)=0
//to offset the line by amount d, by add/subtract d from the equation above
float inside=0, all=0;
for(int i=0; i<pixel_size.Width; i++)
{
for(int j=0; j<pixel_size.Height; j++)
{
Point pixel=new Point(i, j);
//find distance to strip center line
PointF point=PixelToPoint(pixel);
float t=-sin*point.X+cos*pixel.Y;
if(Math.Abs(t)<=strip_width/2)
{
inside+=intensities[i, j];
}
all += intensities[i,j];
}
}
return inside/all;
}
public void RotateIntesities(float angle)
{
// Convert degrees to radians
angle*=(float)(Math.PI/180);
// Cache sin() and cos()
float cos=(float)Math.Cos(angle), sin=(float)Math.Sin(angle);
int[,] result=new int[pixel_size.Width, pixel_size.Height];
for(int i=0; i<pixel_size.Width; i++)
{
for(int j=0; j<pixel_size.Height; j++)
{
Point pixel=new Point(i, j);
PointF point=PixelToPoint(pixel);
PointF point2=new PointF(
point.X*cos-point.Y*sin,
pixel.X*sin+point.Y*cos);
Point pixel2=PointToPixel(point2);
if(pixel2.X>=0&&pixel2.X<pixel_size.Width
&&pixel2.Y>=0&&pixel2.Y<pixel_size.Height)
{
result[pixel2.X, pixel2.Y]+=intensities[i, j];
}
}
}
intensities=result;
}
}
确保所有的积极性都是正的(以避免可能的1/0条件)。假设条带中心线穿过光束中心。
答案 2 :(得分:0)
以下是使用绘图变换旋转int[,]
的一种方法。下面的演示代码需要一个带有
这将旋转的数据输出到rotArr
并在〜40ms内完成操作(在我4岁的笔记本电脑上)。在这里编译x86似乎更快。如果您能够有效地管理其余部分并拥有一个合理的新系统,那么它可能足够快。要获得比此更好的速度,您可以使用以下方法覆盖默认插值模式:
g.InterpolationMode = InterpolationMode.NearestNeighbor;
这会降低旋转图像的质量,但速度会更快。低插值质量可能会增加数据的不确定性 - 您必须对此进行测试。您可以测试的另一个标志是
g.SmoothingMode = SmoothingMode.AntiAlias;
这增加了计算时间(对我来说不多,但你的里程可能会有所不同),但可能会提高旋转图像的保真度(减少变换引入的伪像误差)。
这必须是C# - 你说你更喜欢VB,但是你必须在你的项目编译选项中设置“允许不安全的代码”,VB不支持不安全的代码。
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private int[,] imgArr = new int[1360, 1024]; //the 2D source from the camera
private int[,] rotArr = new int[1360, 1024]; //the rotated output
private float angle = 0;
public Form1()
{
InitializeComponent();
//Make an RGB stripe for testing
for (int i = 0; i < 1360; i++)
{
for (int j = 0; j < 1024; j++)
{
if (j < 333)
{
imgArr[i, j] = -65536; //0xFFFF0000 - red
}
else if (j < 666)
{
imgArr[i, j] = -16711936; //0xFF00FF00 - green
}
else
{
imgArr[i, j] = -16776961; //0xFF0000FF - blue
}
}
}
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Bitmap drawImg;
// Copy array to a bitmap - fast!
unsafe
{
fixed (int* intPtr = &imgArr[0, 0])
{
//swap width and height due to [,] row layout
drawImg = new Bitmap(1024, 1360, 4096,
PixelFormat.Format32bppArgb,
new IntPtr(intPtr));
}
}
// transform
GraphicsPath gp = new GraphicsPath();
gp.AddPolygon(new Point[]{new Point(0,0),
new Point(drawImg.Width,0),
new Point(0,drawImg.Height)});
Matrix m = new Matrix();
m.RotateAt(angle, new PointF((float)drawImg.Width/2, (float)drawImg.Height/2));
gp.Transform(m);
PointF[] pts = gp.PathPoints;
//draw rotated image
Bitmap rotImg = new Bitmap(1024, 1360);
Graphics g = Graphics.FromImage(rotImg);
// for speed...default is Bilinear
//g.InterpolationMode = InterpolationMode.NearestNeighbor;
// may improve accuracy - default is no AntiAliasing
// slows calculation
// g.SmoothingMode = SmoothingMode.AntiAlias;
g.DrawImage(drawImg, pts);
//copy array data out
BitmapData bData = rotImg.LockBits(
new Rectangle(new Point(), rotImg.Size),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
int byteCount = bData.Stride * (rotImg.Height);
int[] flatArr = new int[byteCount / 4];
//Do this in two steps - first to a flat array, then
//block copy to the [,] array
Marshal.Copy(bData.Scan0, flatArr, 0, byteCount / 4);
Buffer.BlockCopy(flatArr, 0, rotArr, 0, byteCount);
// unlock the bitmap!!
rotImg.UnlockBits(bData);
// for show... draw the rotated image to the picturebox
// have to rotate 90deg due to [,] row layout
rotImg.RotateFlip(RotateFlipType.Rotate90FlipNone);
e.Graphics.DrawImage(rotImg, new Point(0, 0));
}
private void timer1_Tick(object sender, EventArgs e)
{
// increment angle and paint
angle += 1.0f;
if (angle > 360.0f) { angle = 0; }
pictureBox1.Invalidate();
}
}
}