如何将鼠标坐标转换为嵌入在较大父容器中的 TransformedBitmap 的像素坐标?
How do I convert from mouse coordinates to pixel coordinates of a TransformedBitmap embedded in a larger parent container?
与 类似,但增加了皱纹,我的 Image
实际上嵌入了一个更大的父 Grid
中,它有背景,我希望像素坐标为悬停在图像边界以外的区域时也很准确。
这是我的 XAML:
<DockPanel>
<Label DockPanel.Dock="Bottom" Name="TheLabel" />
<Grid DockPanel.Dock="Top" Name="TheGrid" Background="Gray" MouseMove="TheGrid_MouseMove">
<Image Name="TheImage" Stretch="Uniform" RenderOptions.BitmapScalingMode="NearestNeighbor" />
</Grid>
</DockPanel>
这是代码:
public MainWindow()
{
InitializeComponent();
const int WIDTH = 4;
const int HEIGHT = 3;
byte[] pixels = new byte[WIDTH * HEIGHT * 3];
// top-left corner red, bottom-right corner blue for orientation
pixels[0] = Colors.Red.B;
pixels[1] = Colors.Red.G;
pixels[2] = Colors.Red.R;
pixels[(WIDTH * (HEIGHT - 1) + (WIDTH - 1)) * 3 + 0] = Colors.Blue.B;
pixels[(WIDTH * (HEIGHT - 1) + (WIDTH - 1)) * 3 + 1] = Colors.Blue.G;
pixels[(WIDTH * (HEIGHT - 1) + (WIDTH - 1)) * 3 + 2] = Colors.Blue.R;
BitmapSource bs = BitmapSource.Create(WIDTH, HEIGHT, 96.0, 96.0, PixelFormats.Bgr24, null, pixels, WIDTH * 3);
TheImage.Source = new TransformedBitmap(bs, new RotateTransform(90.0));
}
private void TheGrid_MouseMove(object sender, MouseEventArgs e)
{
Point p = TheGrid.TranslatePoint(e.GetPosition(TheGrid), TheImage);
if (TheImage.Source is BitmapSource bs)
{
p = new Point(p.X * bs.PixelWidth / TheImage.ActualWidth, p.Y * bs.PixelHeight / TheImage.ActualHeight);
if (TheImage.Source is TransformedBitmap tb)
{
Matrix inverse = tb.Transform.Value;
inverse.Invert();
inverse.OffsetX = 0.0;
inverse.OffsetY = 0.0;
p = inverse.Transform(p);
int w = tb.Source.PixelWidth;
int h = tb.Source.PixelHeight;
p = new Point((p.X + w) % w, (p.Y + h) % h);
}
TheLabel.Content = p.ToString();
}
}
在大多数情况下,这很有效,但如果您将鼠标悬停在旋转图像左侧的灰色区域(大致在下面的屏幕截图中 X 所在的位置),您会得到一个 y 坐标 (0.5),使看起来你在图像中,而实际上你在图像之外,y 坐标应高于图像高度以反映这一点。
这很重要,因为我试图让用户 select 一个 ROI,并且我需要知道 selection 何时超出图像范围,尽管我仍然想允许它。
您可以对图像边界内(例如中心)的“测试点”进行变换,并对变换后的测试点进行取模运算。然后使用变换后的测试点和调整后(取模)测试点之间的偏移来调整实际点。
var p = e.GetPosition(TheImage);
p = new Point(
p.X * bs.PixelWidth / TheImage.ActualWidth,
p.Y * bs.PixelHeight / TheImage.ActualHeight);
if (TheImage.Source is TransformedBitmap tb)
{
var inverse = tb.Transform.Value;
inverse.Invert();
inverse.OffsetX = 0.0;
inverse.OffsetY = 0.0;
var w = tb.Source.PixelWidth;
var h = tb.Source.PixelHeight;
var v = new Vector(bs.PixelWidth / 2, bs.PixelHeight / 2); // test point
var v1 = inverse.Transform(v); // transformed test point
var v2 = new Vector((v1.X + w) % w, (v1.Y + h) % h); // adjusted
p = inverse.Transform(p) - v1 + v2; // add adjusting offset
}
TheLabel.Content = $"x: {p.X:F2}, y: {p.Y:F2}";
与 Image
实际上嵌入了一个更大的父 Grid
中,它有背景,我希望像素坐标为悬停在图像边界以外的区域时也很准确。
这是我的 XAML:
<DockPanel>
<Label DockPanel.Dock="Bottom" Name="TheLabel" />
<Grid DockPanel.Dock="Top" Name="TheGrid" Background="Gray" MouseMove="TheGrid_MouseMove">
<Image Name="TheImage" Stretch="Uniform" RenderOptions.BitmapScalingMode="NearestNeighbor" />
</Grid>
</DockPanel>
这是代码:
public MainWindow()
{
InitializeComponent();
const int WIDTH = 4;
const int HEIGHT = 3;
byte[] pixels = new byte[WIDTH * HEIGHT * 3];
// top-left corner red, bottom-right corner blue for orientation
pixels[0] = Colors.Red.B;
pixels[1] = Colors.Red.G;
pixels[2] = Colors.Red.R;
pixels[(WIDTH * (HEIGHT - 1) + (WIDTH - 1)) * 3 + 0] = Colors.Blue.B;
pixels[(WIDTH * (HEIGHT - 1) + (WIDTH - 1)) * 3 + 1] = Colors.Blue.G;
pixels[(WIDTH * (HEIGHT - 1) + (WIDTH - 1)) * 3 + 2] = Colors.Blue.R;
BitmapSource bs = BitmapSource.Create(WIDTH, HEIGHT, 96.0, 96.0, PixelFormats.Bgr24, null, pixels, WIDTH * 3);
TheImage.Source = new TransformedBitmap(bs, new RotateTransform(90.0));
}
private void TheGrid_MouseMove(object sender, MouseEventArgs e)
{
Point p = TheGrid.TranslatePoint(e.GetPosition(TheGrid), TheImage);
if (TheImage.Source is BitmapSource bs)
{
p = new Point(p.X * bs.PixelWidth / TheImage.ActualWidth, p.Y * bs.PixelHeight / TheImage.ActualHeight);
if (TheImage.Source is TransformedBitmap tb)
{
Matrix inverse = tb.Transform.Value;
inverse.Invert();
inverse.OffsetX = 0.0;
inverse.OffsetY = 0.0;
p = inverse.Transform(p);
int w = tb.Source.PixelWidth;
int h = tb.Source.PixelHeight;
p = new Point((p.X + w) % w, (p.Y + h) % h);
}
TheLabel.Content = p.ToString();
}
}
在大多数情况下,这很有效,但如果您将鼠标悬停在旋转图像左侧的灰色区域(大致在下面的屏幕截图中 X 所在的位置),您会得到一个 y 坐标 (0.5),使看起来你在图像中,而实际上你在图像之外,y 坐标应高于图像高度以反映这一点。
这很重要,因为我试图让用户 select 一个 ROI,并且我需要知道 selection 何时超出图像范围,尽管我仍然想允许它。
您可以对图像边界内(例如中心)的“测试点”进行变换,并对变换后的测试点进行取模运算。然后使用变换后的测试点和调整后(取模)测试点之间的偏移来调整实际点。
var p = e.GetPosition(TheImage);
p = new Point(
p.X * bs.PixelWidth / TheImage.ActualWidth,
p.Y * bs.PixelHeight / TheImage.ActualHeight);
if (TheImage.Source is TransformedBitmap tb)
{
var inverse = tb.Transform.Value;
inverse.Invert();
inverse.OffsetX = 0.0;
inverse.OffsetY = 0.0;
var w = tb.Source.PixelWidth;
var h = tb.Source.PixelHeight;
var v = new Vector(bs.PixelWidth / 2, bs.PixelHeight / 2); // test point
var v1 = inverse.Transform(v); // transformed test point
var v2 = new Vector((v1.X + w) % w, (v1.Y + h) % h); // adjusted
p = inverse.Transform(p) - v1 + v2; // add adjusting offset
}
TheLabel.Content = $"x: {p.X:F2}, y: {p.Y:F2}";