目前我正在使用CIDetector来检测我的UIImage中的矩形。我正在执行建议的方法,将坐标传递到一个过滤器中,以获得一个放在已获取的UIImage上的CIImage。它看起来是这样的:
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)
更新
为了尝试和缩放我的坐标,我又尝试了两件事。
第一:我尝试过使用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)
然后,我用这些点返回了一个新的rect。
第二:我尝试了一个简单的坐标转换,基于图像和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)
没有一个是有效的。我不知道我还能尝试什么。请帮帮忙。我会非常感激的。谢谢!
发布于 2016-07-11 15:40:45
几天来,我一直在努力解决同样的问题,以下是我如何克服这个问题的:
我创建了一个自定义类来存储点,并添加了一些辅助函数:
//
// ObyRectangleFeature.swift
//
// Created by 4oby on 5/20/16.
// Copyright © 2016 cvv. All rights reserved.
//
import Foundation
import UIKit
extension CGPoint {
func scalePointByCeficient(ƒ_x: CGFloat, ƒ_y: CGFloat) -> CGPoint {
return CGPoint(x: self.x/ƒ_x, y: self.y/ƒ_y) //original image
}
func reversePointCoordinates() -> CGPoint {
return CGPoint(x: self.y, y: self.x)
}
func sumPointCoordinates(add: CGPoint) -> CGPoint {
return CGPoint(x: self.x + add.x, y: self.y + add.y)
}
func substractPointCoordinates(sub: CGPoint) -> CGPoint {
return CGPoint(x: self.x - sub.x, y: self.y - sub.y)
}
}
class ObyRectangleFeature : NSObject {
var topLeft: CGPoint!
var topRight: CGPoint!
var bottomLeft: CGPoint!
var bottomRight: CGPoint!
var centerPoint : CGPoint{
get {
let centerX = ((topLeft.x + bottomLeft.x)/2 + (topRight.x + bottomRight.x)/2)/2
let centerY = ((topRight.y + topLeft.y)/2 + (bottomRight.y + bottomLeft.y)/2)/2
return CGPoint(x: centerX, y: centerY)
}
}
convenience init(_ rectangleFeature: CIRectangleFeature) {
self.init()
topLeft = rectangleFeature.topLeft
topRight = rectangleFeature.topRight
bottomLeft = rectangleFeature.bottomLeft
bottomRight = rectangleFeature.bottomRight
}
override init() {
super.init()
}
func rotate90Degree() -> Void {
let centerPoint = self.centerPoint
// /rotate cos(90)=0, sin(90)=1
topLeft = CGPoint(x: centerPoint.x + (topLeft.y - centerPoint.y), y: centerPoint.y + (topLeft.x - centerPoint.x))
topRight = CGPoint(x: centerPoint.x + (topRight.y - centerPoint.y), y: centerPoint.y + (topRight.x - centerPoint.x))
bottomLeft = CGPoint(x: centerPoint.x + (bottomLeft.y - centerPoint.y), y: centerPoint.y + (bottomLeft.x - centerPoint.x))
bottomRight = CGPoint(x: centerPoint.x + (bottomRight.y - centerPoint.y), y: centerPoint.y + (bottomRight.x - centerPoint.x))
}
func scaleRectWithCoeficient(ƒ_x: CGFloat, ƒ_y: CGFloat) -> Void {
topLeft = topLeft.scalePointByCeficient(ƒ_x, ƒ_y: ƒ_y)
topRight = topRight.scalePointByCeficient(ƒ_x, ƒ_y: ƒ_y)
bottomLeft = bottomLeft.scalePointByCeficient(ƒ_x, ƒ_y: ƒ_y)
bottomRight = bottomRight.scalePointByCeficient(ƒ_x, ƒ_y: ƒ_y)
}
func correctOriginPoints() -> Void {
let deltaCenter = self.centerPoint.reversePointCoordinates().substractPointCoordinates(self.centerPoint)
let TL = topLeft
let TR = topRight
let BL = bottomLeft
let BR = bottomRight
topLeft = BL.sumPointCoordinates(deltaCenter)
topRight = TL.sumPointCoordinates(deltaCenter)
bottomLeft = BR.sumPointCoordinates(deltaCenter)
bottomRight = TR.sumPointCoordinates(deltaCenter)
}
}
这是初始化代码:
let scalatedRect : ObyRectangleFeature = ObyRectangleFeature(rectangleFeature)
// fromSize -> Initial size of the CIImage
// toSize -> the size of the scaled Image
let ƒ_x = (fromSize.width/toSize.width)
let ƒ_y = (fromSize.height/toSize.height)
/*the coeficients are interchange intentionally cause of the different
coordinate system used by CIImage and UIImage, you could rotate before
scaling, to preserve the order, but if you do, the result will be offCenter*/
scalatedRect.scaleRectWithCoeficient(ƒ_y, ƒ_y: ƒ_x)
scalatedRect.rotate90Degree()
scalatedRect.correctOriginPoints()
在这一点上,scaleRect
已经准备好以您喜欢的任何方式绘制。
发布于 2016-07-10 07:39:28
如果您只需要显示路径,那么在CAShapeLayer中绘制路径会更容易一些。
为feature.
如果您需要支持缩放图像或带有方向的图像(即来自用户相机的任何内容),则在步骤4中会出现一些复杂情况。
下面是一个例子。支持此代码的前提是图像显示在UIImageView中,contentMode为AspectFit、AspectFill、ScaleToFill或Centre。它还支持图像具有向上、向下、向右和向左的方向。
// 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
}
}
https://stackoverflow.com/questions/38215593
复制相似问题