我正在使用人脸检测服务,给定图片后,我会返回人脸标志位置。我的目标是将这些界标放置在屏幕上显示的图片上。
我的想法是将AbosluteLayout与Image视图一起使用,并将地标放置在图片上。重叠效果很好。问题在于界标的点是指原始图片尺寸坐标,并且渲染的图像具有完全不同的尺寸。我目前正在使用AspectFit,因此完整图片显示在AbsoluteLayout中,该比例通常使用信箱显示,以保持宽高比。
我尝试过的是考虑屏幕密度,并将其应用于这样的原始界标点:
rightEyePoint.X = Convert.ToInt32(rightEyePoint.X / displayDensity);
距离更近,但不是在所有图片上都可以(与风景/肖像不同)。
然后,我想考虑原始图像,可能想知道渲染图像的比例因子,因此我可以转换所有地标点,但是我不知道该怎么做。我试图创建一个自定义图像视图和渲染器以尝试从中获取比例计算,但是没有成功,因为我没有干涉本地图像组件的计算位置:
public class CustomImageView : Image
{
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{
return base.OnMeasure(widthConstraint, heightConstraint);
}
protected override void OnSizeAllocated(double width, double height)
{
base.OnSizeAllocated(width, height);
}
protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
base.OnPropertyChanged(propertyName);
}
}
public class CustomImageViewRenderer : ImageRenderer
{
public CustomImageViewRenderer(Context context) : base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
{
base.OnElementChanged(e);
if(e.NewElement != null)
{
var image = Control as ImageView;
}
}
protected override void OnSizeChanged(int w, int h, int oldw, int oldh)
{
base.OnSizeChanged(w, h, oldw, oldh);
}
protected override Task TryUpdateBitmap(Image previous = null)
{
return base.TryUpdateBitmap(previous);
}
}
然后我想我可以自己计算当前渲染的图像大小,但似乎无法获取。因此,我只得到xamarin表单图像组件的大小,而不是其中呈现的图像。在这一点上,我认为我可以根据纵横比来计算渲染图像的大小,因为我正在使用AspectFit,但在过程中陷入了困境。
axml(示例):
<AbsoluteLayout BackgroundColor="Fuchsia" HorizontalOptions="Fill" VerticalOptions="Fill">
<Controls:PinchToZoomContainer HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" CurrentPosition="{Binding CurrentPhotoPosition}" CurrentScale="{Binding CurrentPhotoScale}" AbsoluteLayout.LayoutBounds="0, 0, 1, 1" AbsoluteLayout.LayoutFlags="All" IsEnabled="{ Binding CurrentStep, Converter={StaticResource checkIntToBooleanValueConverter}, ConverterParameter=2}">
<Image BackgroundColor="Blue" Source="{Binding CurrentPhotoPath}">
<Image.Behaviors>
<behaviors:EventToCommandBehavior
EventName="SizeChanged"
Command="{Binding ImageSizeChangedCommand}"/>
</Image.Behaviors>
</Image>
<!--<Controls:CustomImageView Source="{Binding CurrentPhotoPath}" BackgroundColor="Purple" SizeChanged=""/-->
</Controls:PinchToZoomContainer>
<!-- calculated landmarks -->
<Controls:LandmarkView
Padding="2"
DragMode="TouchAndRelease"
LBounds="{Binding HairStartLandmarkBounds, Mode=TwoWay}"
DragDirection="Vertical"
HorizontalOptions="FillAndExpand"
VerticalOptions="Start"
IsVisible="{ Binding CurrentStep, Converter={StaticResource checkIntToBooleanValueConverter}, ConverterParameter=3}"
x:Name="HairStartLandmark"
ToggleDraggingCommand="{Binding HairStartLandmarkTouchedCommand}">
<Controls:LandmarkView.Content>
<AbsoluteLayout>
<Image BackgroundColor="Red"
WidthRequest="{Binding LandmarkLineWidth}"
AbsoluteLayout.LayoutBounds= "{Binding LandmarkLineXPos}"
AbsoluteLayout.LayoutFlags="YProportional,WidthProportional"/>
<ffimageloading:CachedImage
WidthRequest="{Binding HairStartLandmarkIconSize}"
HeightRequest="{Binding HairStartLandmarkIconSize}"
Source="{Binding HairStartLandmarkIcon, Converter={StaticResource SvgImageSourceConverter}}">
</ffimageloading:CachedImage>
</AbsoluteLayout>
</Controls:LandmarkView.Content>
</Controls:LandmarkView>
</AbsoluteLayout>
所以我的问题是,如何才能轻松获得渲染图像坐标系的原点和比例因子,以将其应用于界标点坐标并将其正确定位在渲染图像上?
更新:
我添加一张我所拥有的和我所需要的当前状态的图像。蓝色背景是Xamarin.Forms图像视图背景:
非常感谢您
答案 0 :(得分:1)
一些数学传入。
Xamarin Forms使用与设备无关的像素,所以我的猜测是,您需要在帧内渲染图像的比例。假设其 A1:A2 其中一个单元长 x 。给出了帧的比例,例如 B1:B2 ,其中一个单位长 y 。我们的问题是x未知。因此,我们需要从等式中删除它(字面:D)
首先,您需要确定哪个更大,A1或A2。为什么?因为较长的一侧将成为完全适合框架的一侧。 (例如,如果图像的宽度大于高度,则图像的顶部和底部将具有信箱。如果图像的高度大于宽度,则图像的左侧和右侧将具有信箱。) A2较大(表示图片的高度大于宽度)。
在这种情况下, A2 * x 应该等于 B2 * y 。因此, x = B2 / A2 * y
您的图片将位于框架的中间,因此框架的另一侧将像:
[{z}宽蓝色背景]-[图像宽度]-[{z}宽蓝色背景]
现在,我们只需要获取 z 的长度。就是(B1 * y-(B2 / A2 * y)* A1)/ 2。
背景框架的大小:600 x 400(宽x高)-可以从代码中检索-表示B1 = 3,B2 = 2和y = 200。
图像比例:4:5-可以从原始图像尺寸中获取-表示A1 = 4,A2 = 5,我们不知道x是多少。 (因为这是您需要的一部分。)
这将导致x = 2/5 * 200 = 80。
这最终意味着z为(3 * 200-80 * 4)/ 2 =(600-320)/ 2 = 140。
这意味着,在这种情况下,图像的第一个倾斜是在背景框架内的(140,0)位置。
我希望这会有所帮助,并让您创建自己的计算方法。
答案 1 :(得分:0)
最后,我使用了一个更简单的系统。这是我创建的用于获取渲染图像边界的方法的代码。
private void CalculateRendredImageBounds()
{
Point origin = new Point();
_renderedImageRatio = CurrentPhotoSize.Width / CurrentPhotoSize.Height;
if (CurrentPhotoSize.Height > CurrentPhotoSize.Width)
{
//case height greater than width (portrait)
_renderedPhotoWidth = (CurrentPhotoSize.Width * RenderedImageContainerSize.Height) / CurrentPhotoSize.Height;
origin.X = Convert.ToInt32((RenderedImageContainerSize.Width - _renderedPhotoWidth) / 2);
origin.Y = 0;
_renderedImageBounds = new Rectangle(origin.X, origin.Y, _renderedPhotoWidth, RenderedImageContainerSize.Height);
}
else
{
//case width greater than height (landscape)
_renderedPhotoHeight = (CurrentPhotoSize.Height * RenderedImageContainerSize.Width) / CurrentPhotoSize.Width;
origin.X = 0;
origin.Y = Convert.ToInt32((RenderedImageContainerSize.Height - _renderedPhotoHeight) / 2);
_renderedImageBounds = new Rectangle(origin.X, origin.Y, RenderedImageContainerSize.Width, _renderedPhotoHeight);
}
}
然后,当您想在容器坐标系中找到一个点时,可以使用以下内容
X景观:
Convert.ToInt32((originalXPos * RenderedImageContainerSize.Width) / CurrentPhotoSize.Width);
Y风景:
Convert.ToInt32((originalYPos * _renderedPhotoHeight) / CurrentPhotoSize.Height + (RenderedImageContainerSize.Height - _renderedPhotoHeight) / 2);
X肖像:
Convert.ToInt32((originalXPos * _renderedPhotoWidth) / CurrentPhotoSize.Width + (RenderedImageContainerSize.Width - _renderedPhotoWidth) / 2);
Y人像:
Convert.ToInt32((originalYPos * RenderedImageContainerSize.Height) / CurrentPhotoSize.Height);
注意:我没有显示私有属性声明,但是我认为代码足够可读。