gpt4 book ai didi

ios - 用 UIBezierPath 表示 CIRectangleFeature - Swift

转载 作者:IT王子 更新时间:2023-10-29 05:11:24 24 4
gpt4 key购买 nike

目前我正在使用 CIDetector 来检测我的 UIImage 中的矩形。我正在按照建议的方式将坐标传递到过滤器中,以取回 CIImage 以覆盖获取的 UIImage。它看起来像这样:

func performRectangleDetection(image: UIKit.CIImage) -> UIKit.CIImage? {
var resultImage: UIKit.CIImage?
let detector:CIDetector = CIDetector(ofType: CIDetectorTypeRectangle, context: nil, options: [CIDetectorAccuracy : CIDetectorAccuracyHigh])
// Get the detections
let features = detector.featuresInImage(image)
for feature in features as! [CIRectangleFeature] {
resultImage = self.drawHighlightOverlayForPoints(image, topLeft: feature.topLeft, topRight: feature.topRight,
bottomLeft: feature.bottomLeft, bottomRight: feature.bottomRight)
}
return resultImage

}


func drawHighlightOverlayForPoints(image: UIKit.CIImage, topLeft: CGPoint, topRight: CGPoint,
bottomLeft: CGPoint, bottomRight: CGPoint) -> UIKit.CIImage {

var overlay = UIKit.CIImage(color: CIColor(red: 1.0, green: 0.55, blue: 0.0, alpha: 0.45))
overlay = overlay.imageByCroppingToRect(image.extent)
overlay = overlay.imageByApplyingFilter("CIPerspectiveTransformWithExtent",
withInputParameters: [
"inputExtent": CIVector(CGRect: image.extent),
"inputTopLeft": CIVector(CGPoint: topLeft),
"inputTopRight": CIVector(CGPoint: topRight),
"inputBottomLeft": CIVector(CGPoint: bottomLeft),
"inputBottomRight": CIVector(CGPoint: bottomRight)
])
return overlay.imageByCompositingOverImage(image)
}

调用 performRectangleDetection 通过 CIImage 显示检测到的矩形。

它看起来像上面的图片。我需要使用设置为描边的 UIBezierPath 来显示相同​​的红色矩形。我需要这个,以便用户可以调整检测,以防它不是 100% 准确。我试图画出一条路径,但没有成功。这是我绘制路径的方式。我使用一个名为 rect 的自定义类来保存 4 个点。这是检测:

func detectRect() -> Rect{
var rect:Rect?
let detector:CIDetector = CIDetector(ofType: CIDetectorTypeRectangle, context: nil, options: [CIDetectorAccuracy : CIDetectorAccuracyHigh])
// Get the detections
let features = detector.featuresInImage(UIKit.CIImage(image: self)!)
for feature in features as! [CIRectangleFeature] {
rect = Rect(tL: feature.topLeft, tR: feature.topRight, bR: feature.bottomRight, bL: feature.bottomLeft)
}
return rect!
}

接下来,我必须缩放坐标。这是 Rect 类中的函数:

func scaleRect(image:UIImage, imageView:UIImageView) ->Rect{

let scaleX = imageView.bounds.width/image.size.width
var tlx = topLeft.x * scaleX
var tly = topLeft.y * scaleX
tlx += (imageView.bounds.width - image.size.width * scaleX) / 2.0
tly += (imageView.bounds.height - image.size.height * scaleX) / 2.0
let tl = CGPointMake(tlx, tly)

var trx = topRight.x * scaleX
var trY = topRight.y * scaleX
trx += (imageView.bounds.width - image.size.width * scaleX) / 2.0
trY += (imageView.bounds.height - image.size.height * scaleX) / 2.0
let tr = CGPointMake(trx, trY)

var brx = bottomRight.x * scaleX
var bry = bottomRight.y * scaleX
brx += (imageView.bounds.width - image.size.width * scaleX) / 2.0
bry += (imageView.bounds.height - image.size.height * scaleX) / 2.0
let br = CGPointMake(brx, bry)

var blx = bottomLeft.x * scaleX
var bly = bottomLeft.y * scaleX
blx += (imageView.bounds.width - image.size.width * scaleX) / 2.0
bly += (imageView.bounds.height - image.size.height * scaleX) / 2.0
let bl = CGPointMake(blx, bly)

let rect = Rect(tL: tl, tR: tr, bR: br, bL: bl)
return rect
}

最后画出路径:

var tet = image.detectRect()
tet = tet.scaleRect(image, imageView: imageView)
let shapeLayer = CAShapeLayer()
let path = ViewController.drawPath(tet.topLeft, p2: tet.topRight, p3: tet.bottomRight, p4: tet.bottomLeft)
shapeLayer.path = path.CGPath
shapeLayer.lineWidth = 5
shapeLayer.fillColor = nil
shapeLayer.strokeColor = UIColor.orangeColor().CGColor
imageView.layer.addSublayer(shapeLayer)

该路径已超出屏幕且不准确。我知道我必须将坐标从 CoreImage 坐标调整到 UIKit 坐标,然后为 UIImageView 缩放它们。不幸的是,我不知道如何正确地做到这一点。我知道我可以重复使用我的一些检测代码来完成此操作,但我不知道要采取的正确步骤。任何帮助,将不胜感激!谢谢。这是正在发生的事情的示例:

更新

为了测试我在 scaleRect() 中执行的缩放,我决定使我的 ImageView 大小与我的图像大小相同。然后我打印了缩放前后的坐标。我认为因为它们是相同的,所以我的缩放比例是正确的。这是代码:

var tet = image.detectRect()
//Before scaling
print(tet.topLeft)
print(tet.topRight)
print(tet.bottomRight)
print(tet.bottomLeft)
print("**************************************************")
//After scaling
tet = tet.scaleRect(image, imageView: imageView)
print(tet.topLeft)
print(tet.topRight)
print(tet.bottomRight)
print(tet.bottomLeft)

这是输出:

(742.386596679688, 927.240844726562)

(1514.93835449219, 994.811096191406)

(1514.29675292969, 155.2802734375)

(741.837524414062, 208.55403137207)


(742.386596679688, 927.240844726562)

(1514.93835449219, 994.811096191406)

(1514.29675292969, 155.2802734375)

(741.837524414062, 208.55403137207)

更新

为了尝试和缩放我的坐标,我又尝试了两件事。

1 号:我尝试使用 UIView convertPoint 函数将点从图像转换为 UIImageView。这是我的编码方式:我用

替换了 scaleRect() 函数
let view_image = UIView(frame: CGRectMake(0, 0, image.size.width, image.size.height))
let tL = view_image.convertPoint(self.topLeft, toView: imageView)
let tR = view_image.convertPoint(self.topRight, toView: imageView)
let bR = view_image.convertPoint(self.bottomRight, toView: imageView)
let bL = view_image.convertPoint(self.bottomLeft, toView: imageView)

然后我用这些点返回了一个新的矩形。

2 号:我尝试根据图像和imageView的宽度和高度的差异对坐标进行简单的翻译。继承人的代码:

 let widthDiff = (image.size.width - imageView.frame.size.width)
let highDiff = (image.size.height - imageView.frame.size.height)

let tL = CGPointMake(self.topLeft.x-widthDiff, self.topLeft.y-highDiff)
let tR = CGPointMake(self.topRight.x-widthDiff, self.topRight.y-highDiff)
let bR = CGPointMake(self.bottomRight.x-widthDiff, self.bottomRight.y-highDiff)
let bL = CGPointMake(self.bottomLeft.x-widthDiff, self.bottomLeft.y-highDiff)

更新我也尝试过使用 CGAffineTransform。代码:

var transform = CGAffineTransformMakeScale(1, -1)
transform = CGAffineTransformTranslate(transform, 0, -imageView.bounds.size.height)
let tL = CGPointApplyAffineTransform(self.topLeft, transform)
let tR = CGPointApplyAffineTransform(self.topRight, transform)
let bR = CGPointApplyAffineTransform(self.bottomRight, transform)
let bL = CGPointApplyAffineTransform(self.bottomLeft, transform)

没有一个有效。我不知道我还能尝试什么。请帮忙。这将不胜感激。谢谢!

最佳答案

如果您只需要显示路径,那么在 CAShapeLayer 中绘制路径会更容易一些。

  1. 将 CAShapeLayer 添加到预览图像。
  2. 计算矩形。
  3. 为该功能创建一个 UIBezierPath。
  4. 转换路径以匹配源图像。
  5. 设置CAShapeLayer的路径

如果您需要支持缩放图像或具有方向的图像(即来自用户相机的任何图像),则在第 4 步中会出现一些复杂情况。

下面是一个例子。这支持此代码假定图像显示在 contentMode 为 AspectFit、AspectFill、ScaleToFill 或 Centre 的 UIImageView 中。它还支持方向为上、下、右和左的图像。

// Extension for calculating the image scale in an image view.
// See: http://stackoverflow.com/questions/6856879/iphone-getting-the-size-of-an-image-after-aspectft
extension UIImageView {

var imageScale: CGSize? {

guard let image = image else {
return nil
}

let sx = Double(self.frame.size.width / image.size.width)
let sy = Double(self.frame.size.height / image.size.height)
var s = 1.0
switch (self.contentMode) {
case .ScaleAspectFit:
s = fmin(sx, sy)
return CGSize (width: s, height: s)

case .ScaleAspectFill:
s = fmax(sx, sy)
return CGSize(width:s, height:s)

case .ScaleToFill:
return CGSize(width:sx, height:sy)

default:
return CGSize(width:s, height:s)
}
}
}

// Extension which provides a transform to rotate the image based on it's orientation metadata.
extension UIImageView {

var normalizedTransformForOrientation: CGAffineTransform? {

guard let image = image else {
return nil
}

let r: CGFloat

switch image.imageOrientation {

case .Up:
r = 0

case .Down:
r = +1.0

case .Left:
r = -0.5

case .Right:
r = +0.5

default:
fatalError()
}

let cx = CGRectGetMidX(bounds)
let cy = CGRectGetMidY(bounds)

var transform = CGAffineTransformIdentity
transform = CGAffineTransformTranslate(transform, cx, cy)
transform = CGAffineTransformRotate(transform, CGFloat(M_PI) * r)
transform = CGAffineTransformTranslate(transform, -cx, -cy)
return transform
}
}

class ViewController: UIViewController {

// Shape layer for displaying the path.
let pathLayer: CAShapeLayer = {
let layer = CAShapeLayer()
layer.fillColor = UIColor.greenColor().colorWithAlphaComponent(0.3).CGColor
layer.strokeColor = UIColor.greenColor().colorWithAlphaComponent(0.9).CGColor
layer.lineWidth = 2.0
return layer
}()

// Image view where the preview and path overlay will be displayed.
@IBOutlet var imageView: UIImageView?

override func viewDidLoad() {

super.viewDidLoad()

// Add the path overlay to the image view.
imageView?.layer.addSublayer(pathLayer)

// Load a sample image from the assets.
selectImage(UIImage(named: "sample"))
}

func selectImage(image: UIImage?) {

imageView?.image = image

if let image = image {
processImage(image)
}
}

// Detect rectangles in image, and draw the path on the screen.
func processImage(input: UIImage) {

let path = pathsForRectanglesInImage(input)

let transform = pathTransformForImageView(imageView!)
path?.applyTransform(transform)

pathLayer.path = path?.CGPath
}

// Detect rectangles in an image and return a UIBezierPath.
func pathsForRectanglesInImage(input: UIImage) -> UIBezierPath? {

guard let sourceImage = CIImage(image: input) else {
return nil
}

let features = performRectangleDetection(sourceImage)

return pathForFeatures(features)
}

// Detect rectangles in image.
func performRectangleDetection(image: CIImage) -> [CIFeature] {

let detector:CIDetector = CIDetector(
ofType: CIDetectorTypeRectangle,
context: nil,
options: [CIDetectorAccuracy : CIDetectorAccuracyHigh]
)

let features = detector.featuresInImage(image)

return features
}

// Compose a UIBezierPath from CIRectangleFeatures.
func pathForFeatures(features: [CIFeature]) -> UIBezierPath {

let path = UIBezierPath()

for feature in features {

guard let rect = feature as? CIRectangleFeature else {
continue
}

path.moveToPoint(rect.topLeft)
path.addLineToPoint(rect.topRight)
path.addLineToPoint(rect.bottomRight)
path.addLineToPoint(rect.bottomLeft)
path.closePath()
}

return path
}

// Calculate the transform to orient the preview path to the image shown inside the image view.
func pathTransformForImageView(imageView: UIImageView) -> CGAffineTransform {

guard let image = imageView.image else {
return CGAffineTransformIdentity
}

guard let imageScale = imageView.imageScale else {
return CGAffineTransformIdentity
}

guard let imageTransform = imageView.normalizedTransformForOrientation else {
return CGAffineTransformIdentity
}

let frame = imageView.frame

let imageWidth = image.size.width * imageScale.width
let imageHeight = image.size.height * imageScale.height

var transform = CGAffineTransformIdentity

// Rotate to match the image orientation.
transform = CGAffineTransformConcat(imageTransform, transform)

// Flip vertically (flipped in CIDetector).
transform = CGAffineTransformTranslate(transform, 0, CGRectGetHeight(frame))
transform = CGAffineTransformScale(transform, 1.0, -1.0)

// Centre align.
let tx: CGFloat = (CGRectGetWidth(frame) - imageWidth) * 0.5
let ty: CGFloat = (CGRectGetHeight(frame) - imageHeight) * 0.5
transform = CGAffineTransformTranslate(transform, tx, ty)

// Scale to match UIImageView scaling.
transform = CGAffineTransformScale(transform, imageScale.width, imageScale.height)

return transform
}
}

Detected rectangle with stroked overlay

关于ios - 用 UIBezierPath 表示 CIRectangleFeature - Swift,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38215593/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com