使用 VNRectangleObservation 裁剪 UIImage

Posted

技术标签:

【中文标题】使用 VNRectangleObservation 裁剪 UIImage【英文标题】:Cropping UIImage Using VNRectangleObservation 【发布时间】:2019-12-30 22:36:09 【问题描述】:

我正在使用 Vision 框架来检测拍摄照片中的矩形文档。检测并在文档周围绘制路径非常有效。然后我想将图像裁剪为仅检测到的文档。我已成功裁剪图像,但似乎坐标没有对齐,裁剪的图像只是检测到的文档的一部分,其余的只是文档后面的桌子。我正在使用以下裁剪代码:

private UIImage CropImage(UIImage image, CGRect rect, float scale)

    var drawRect = new CGRect(rect.X, rect.Y, rect.Size.Width, rect.Size.Height);
    using (var cgImage = image.CGImage.WithImageInRect(drawRect))
    
        var croppedImage = UIImage.FromImage(cgImage);
        return croppedImage;
    ;

使用以下参数:

图像与我成功绘制矩形路径的 UIImage 相同。

rect 是 VNRectangleObservation.BoundingBox。这是标准化的,所以我使用 image.size 对其进行缩放。这与绘制矩形路径时的缩放比例相同。

比例为 1f,但我目前忽略了这一点。

裁剪后的图像通常看起来是正确的尺寸,但它向上和向左移动,从而切断了文档的下部和右侧。任何帮助将不胜感激。

【问题讨论】:

嗨,您能否分享有问题的原始图像,并与实际截图和想要的截图更好地分享,这将有所帮助。如果图像的原始尺寸不适合裁剪尺寸,则不会显示预期。 【参考方案1】:

对于发现此问题的任何其他人,问题似乎是 CGImage 在裁剪图像时旋转导致 VNRectangleObservation 不再排队。我使用这篇文章Tracking and Altering Images 来获得使用CIFilter 的有效解决方案。裁剪代码如下:

var ciFilter = CIFilter.FromName("CIPerspectiveCorrection");
if (ciFilter == null) continue;

var width = inputImage.Extent.Width;
var height = inputImage.Extent.Height;
var topLeft = new CGPoint(observation.TopLeft.X * width, observation.TopLeft.Y * height);
var topRight = new CGPoint(observation.TopRight.X * width, observation.TopRight.Y * height);
var bottomLeft = new CGPoint(observation.BottomLeft.X * width, observation.BottomLeft.Y * height);
var bottomRight = new CGPoint(observation.BottomRight.X * width, observation.BottomRight.Y * height);

ciFilter.SetValueForKey(new CIVector(topLeft), new NSString("inputTopLeft"));
ciFilter.SetValueForKey(new CIVector(topRight), new NSString("inputTopRight"));
ciFilter.SetValueForKey(new CIVector(bottomLeft), new NSString("inputBottomLeft"));
ciFilter.SetValueForKey(new CIVector(bottomRight), new NSString("inputBottomRight"));

var ciImage = inputImage.CreateByApplyingOrientation(CGImagePropertyOrientation.Up);
ciFilter.SetValueForKey(ciImage, CIFilterInputKey.Image);
var outputImage = ciFilter.OutputImage;
var uiImage = new UIImage(outputImage);
imageList.Add(uiImage);

imageListList<UImage>,因为我正在处理多个检测到的矩形。

observationVNRectangleObservation 类型的单个观察。

【讨论】:

【参考方案2】:

裁剪后的图像通常看起来是正确的尺寸,但它向上和向左移动,从而切断了文档的下部和右侧。

来自 Apple 文档 CGImageCreateWithImageInRect ,有关于裁剪尺寸的讨论。

CGImageCreateWithImageInRect 执行以下任务来创建子图像:

调用CGRectIntegral函数将rect参数调整为整数范围。

它与矩形相交,矩形的原点为(0,0),大小等于image参数指定的图片大小。

它读取生成的矩形内的像素,将其中的第一个像素视为子图像的原点。

如果WH分别是图像的宽度和高度,那么点(0,0)对应于图像数据的第一个像素。点(W–1, 0)是图像数据第一行的最后一个像素,(0, H–1)是图像数据最后一行的第一个像素,(W–1, H–1)是图像数据最后一行的最后一个像素.

然后您可以使用图像(大小为:1920 * 1080)签入您的本地项目,如下所示:

UIImageView imageView = new UIImageView(new CGRect(0, 400, UIScreen.MainScreen.Bounds.Size.Width, 300));
UIImage image = new UIImage("th.jpg");
imageView.Image = CropImage(image, new CGRect(0, 0, 1920, 1080), 1);
View.AddSubview(imageView);

CropImage 方法:

    private UIImage CropImage(UIImage image, CGRect rect, float scale)

    var drawRect = new CGRect(rect.X, rect.Y, rect.Size.Width, rect.Size.Height);
    using (var cgImage = image.CGImage.WithImageInRect(drawRect))
    
        if(null != cgImage)
        
            var croppedImage = UIImage.FromImage(cgImage);
            return croppedImage;
        
        else
        
            return image;
        

    ;

这将显示图像的原始大小:

现在您可以修改裁剪后的尺寸如下:

UIImageView imageView = new UIImageView(new CGRect(0, 400, UIScreen.MainScreen.Bounds.Size.Width, 300));
UIImage image = new UIImage("th.jpg");
imageView.Image = CropImage(image, new CGRect(0, 0, 1920, 100), 1);
View.AddSubview(imageView);

这里我设置了x = 0y = 0,即从(0,0)开始,宽度为1920,高度为100。我只是裁剪原始 Image 的高度。效果如下:

那么如果你修改x/y,裁剪后的图像会移动到其他区域进行裁剪。如下:

UIImageView imageView = new UIImageView(new CGRect(0, 400, UIScreen.MainScreen.Bounds.Size.Width, 300));
UIImage image = new UIImage("th.jpg");
imageView.Image = CropImage(image, new CGRect(0, 100, 1920, 100), 1);
View.AddSubview(imageView);

然后你会看到它与第二个效果不同:

所以在裁剪图像时,你应该清楚地了解image.CGImage.WithImageInRect(drawRect)drawRect

注意来自文档:

即使UIImageView 仅显示缩放版本,请务必指定子矩形相对于原始图像的完整大小的坐标。

【讨论】:

感谢您的深入回复。不幸的是,种植对我来说从来都不是问题。我相信我的问题源于多种因素。 1. CGImage的东西左下角是0,0,而UIImage的左上角是0,0。 2. 创建 UIImage 时,我正在做一些定位工作。删除似乎有帮助。 3. ImageView 正在做一些它自己的缩放,这让事情变得困难。解决这些问题让我每次都能获得可靠的好收成。感谢您抽出宝贵时间回复。 @ottermatic 这是个好问题,对其他人会有帮助。如果使用CGRectCGImageUIImage 设置rect,则(0,0) 全部位于左上角。如果回答对你有帮助,有时间可以标记一下:)

以上是关于使用 VNRectangleObservation 裁剪 UIImage的主要内容,如果未能解决你的问题,请参考以下文章

第一篇 用于测试使用

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份