Skip to content

Commit 6fa258e

Browse files
authored
Merge pull request #1792 from Countonme/image-alignment-tests
Add ImageAlignment sample using ORB and affine transform
2 parents c8b8160 + 5b095b1 commit 6fa258e

2 files changed

Lines changed: 169 additions & 0 deletions

File tree

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Drawing;
4+
using System.Drawing.Imaging;
5+
using System.Linq;
6+
using System.Runtime.InteropServices;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
10+
namespace OpenCvSharp.Tests.Img_alignment;
11+
12+
internal class ImageAlignmentTests
13+
{
14+
private static Mat templateMatGray;
15+
16+
/// <summary>
17+
/// 初始化模板,只需调用一次
18+
/// </summary>
19+
public static void InitTemplate(Bitmap templateBitmap)
20+
{
21+
templateMatGray = BitmapToMat(templateBitmap);
22+
Cv2.CvtColor(templateMatGray, templateMatGray, ColorConversionCodes.BGR2GRAY);
23+
}
24+
25+
/// <summary>
26+
/// 每帧对齐到模板
27+
/// </summary>
28+
public static Bitmap AlignToTemplate(Bitmap srcBitmap)
29+
{
30+
if (srcBitmap == null || templateMatGray == null)
31+
return srcBitmap;
32+
33+
Mat srcMat = BitmapToMat(srcBitmap);
34+
Mat srcGray = new Mat();
35+
Cv2.CvtColor(srcMat, srcGray, ColorConversionCodes.BGR2GRAY);
36+
37+
// ORB 特征检测
38+
var orb = ORB.Create(500);
39+
KeyPoint[] kpTemplate, kpSrc;
40+
Mat desTemplate = new Mat();
41+
Mat desSrc = new Mat();
42+
orb.DetectAndCompute(templateMatGray, null, out kpTemplate, desTemplate);
43+
orb.DetectAndCompute(srcGray, null, out kpSrc, desSrc);
44+
45+
// BFMatcher 特征匹配
46+
var bf = new BFMatcher(NormTypes.Hamming, crossCheck: true);
47+
var matches = bf.Match(desTemplate, desSrc);
48+
if (matches.Length < 4)
49+
return srcBitmap; // 匹配点太少,直接返回原图
50+
51+
Array.Sort(matches, (a, b) => a.Distance.CompareTo(b.Distance));
52+
53+
var ptsTemplate = new Point2f[matches.Length];
54+
var ptsSrc = new Point2f[matches.Length];
55+
for (int i = 0; i < matches.Length; i++)
56+
{
57+
ptsTemplate[i] = kpTemplate[matches[i].QueryIdx].Pt;
58+
ptsSrc[i] = kpSrc[matches[i].TrainIdx].Pt;
59+
}
60+
61+
// 仿射矩阵
62+
Mat mask = new Mat();
63+
Mat M = Cv2.EstimateAffinePartial2D(InputArray.Create(ptsSrc), InputArray.Create(ptsTemplate), mask);
64+
if (M.Empty())
65+
return srcBitmap;
66+
67+
Mat aligned = new Mat();
68+
Cv2.WarpAffine(srcMat, aligned, M, new Size(templateMatGray.Width, templateMatGray.Height));
69+
70+
return MatToBitmap(aligned);
71+
}
72+
73+
#region Bitmap <-> Mat (无需 Extensions)
74+
75+
public static Mat BitmapToMat(Bitmap bitmap)
76+
{
77+
using (var ms = new System.IO.MemoryStream())
78+
{
79+
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
80+
byte[] bytes = ms.ToArray();
81+
return Cv2.ImDecode(bytes, ImreadModes.Color); // 彩色
82+
}
83+
}
84+
85+
public static Bitmap MatToBitmap(Mat mat)
86+
{
87+
if (mat == null || mat.Empty())
88+
return null;
89+
90+
Bitmap bmp;
91+
92+
if (mat.Channels() == 1)
93+
{
94+
// 灰度图
95+
bmp = new Bitmap(mat.Width, mat.Height, PixelFormat.Format8bppIndexed);
96+
BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),
97+
ImageLockMode.WriteOnly, bmp.PixelFormat);
98+
99+
// 创建缓冲区
100+
int length = mat.Width * mat.Height;
101+
byte[] buffer = new byte[length];
102+
Marshal.Copy(mat.Data, buffer, 0, length);
103+
104+
// 拷贝到 Bitmap
105+
Marshal.Copy(buffer, 0, data.Scan0, length);
106+
bmp.UnlockBits(data);
107+
108+
// 设置灰度调色板
109+
ColorPalette palette = bmp.Palette;
110+
for (int i = 0; i < 256; i++)
111+
palette.Entries[i] = Color.FromArgb(i, i, i);
112+
bmp.Palette = palette;
113+
}
114+
else if (mat.Channels() == 3)
115+
{
116+
// 彩色图 BGR -> RGB
117+
Mat rgbMat = new Mat();
118+
Cv2.CvtColor(mat, rgbMat, ColorConversionCodes.BGR2RGB);
119+
120+
bmp = new Bitmap(rgbMat.Width, rgbMat.Height, PixelFormat.Format24bppRgb);
121+
BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),
122+
ImageLockMode.WriteOnly, bmp.PixelFormat);
123+
124+
int length = rgbMat.Rows * int.Parse(rgbMat.Step().ToString());
125+
byte[] buffer = new byte[length];
126+
Marshal.Copy(rgbMat.Data, buffer, 0, length);
127+
Marshal.Copy(buffer, 0, data.Scan0, length);
128+
129+
bmp.UnlockBits(data);
130+
}
131+
else
132+
{
133+
throw new NotSupportedException("Unsupported Mat format: channels=" + mat.Channels());
134+
}
135+
136+
return new Bitmap(bmp); // 返回安全副本
137+
}
138+
139+
#endregion
140+
}
141+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Drawing;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
8+
namespace OpenCvSharp.Tests.Img_alignment;
9+
10+
internal class Program
11+
{
12+
static void Main()
13+
{
14+
Console.WriteLine("=== Image Alignment Demo ===");
15+
16+
// 初始化模板
17+
Bitmap template = new Bitmap("template.jpg");
18+
ImageAlignmentTests.InitTemplate(template);
19+
20+
// 模拟每帧输入(摄像头帧)
21+
Bitmap frame = new Bitmap("input.jpg");
22+
23+
Bitmap aligned = ImageAlignmentTests.AlignToTemplate(frame);
24+
25+
aligned.Save("aligned_output.jpg");
26+
Console.WriteLine("Aligned image saved!");
27+
}
28+
}

0 commit comments

Comments
 (0)