Vous pouvez toujours recadrer visuellement n'importe quelle image dans un quadrilatère (une forme à quatre côtés - ne doit pas nécessairement être un rectangle) à l'aide d'un filtre d'image de base appelé CIPerspectiveCorrection. Imaginons que vous ayez un cadre imageView de 414 de largeur sur 716 de hauteur, avec une image de largeur 1600 sur 900 de hauteur. (Vous utilisez un mode contenu de .aspectFit, n'est-ce pas?) Supposons que vous vouliez recadrer une forme à 4 côtés qui est des coins - dans (X, Y) coordonnées dans l'imageView - are (50,50), (75,75) (100 300) et (25 200). Notez que je liste les points en haut à gauche (TL, en haut à droite (TR), en bas à droite (BR), en bas à gauche (BL) et notez que ce n'est pas un rectangle rectiligne
(1) Convertissez le UIImage en un CIImage où le "extent" est la taille UIImage, (2) Convertissez ces coordonnées UIImageView en coordonnées CIImage, (3) passez-les et le CIImage dans le filtre CIPerspectiveCorrection pour le recadrage et (4) rendre la sortie de CIImage en UIImageView.
est un code ci-dessous que j'ai jeté ensemble. Il fonctionne. Il est aussi un peu rude sur les bords, mais nous espérons que vous obtenez le concept.
class ViewController: UIViewController {
let uiTL = CGPoint(x: 50, y: 50)
let uiTR = CGPoint(x: 75, y: 75)
let uiBL = CGPoint(x: 100, y: 300)
let uiBR = CGPoint(x: 25, y: 200)
var ciImage:CIImage!
var ctx:CIContext!
@IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
ctx = CIContext(options: nil)
ciImage = CIImage(image: imageView.image!)
}
override func viewWillLayoutSubviews() {
let ciTL = createVector(createScaledPoint(uiTL))
let ciTR = createVector(createScaledPoint(uiTR))
let ciBR = createVector(createScaledPoint(uiBR))
let ciBL = createVector(createScaledPoint(uiBL))
imageView.image = doPerspectiveCorrection(CIImage(image: imageView.image!)!,
context: ctx,
topLeft: ciTL,
topRight: ciTR,
bottomRight: ciBR,
bottomLeft: ciBL)
}
func doPerspectiveCorrection(
_ image:CIImage,
context:CIContext,
topLeft:AnyObject,
topRight:AnyObject,
bottomRight:AnyObject,
bottomLeft:AnyObject)
-> UIImage {
let filter = CIFilter(name: "CIPerspectiveCorrection")
filter?.setValue(topLeft, forKey: "inputTopLeft")
filter?.setValue(topRight, forKey: "inputTopRight")
filter?.setValue(bottomRight, forKey: "inputBottomRight")
filter?.setValue(bottomLeft, forKey: "inputBottomLeft")
filter!.setValue(image, forKey: kCIInputImageKey)
let cgImage = context.createCGImage((filter?.outputImage)!, from: (filter?.outputImage!.extent)!)
return UIImage(cgImage: cgImage!)
}
func createScaledPoint(_ pt:CGPoint) -> CGPoint {
let x = (pt.x/imageView.frame.width) * ciImage.extent.width
let y = (pt.y/imageView.frame.height) * ciImage.extent.height
return CGPoint(x: x, y: y)
}
func createVector(_ point:CGPoint) -> CIVector {
return CIVector(x: point.x, y: ciImage.extent.height - point.y)
}
func createPoint(_ vector:CGPoint) -> CGPoint {
return CGPoint(x: vector.x, y: ciImage.extent.height - vector.y)
}
}
EDIT: Je mets ceci ici pour expliquer les choses. J'espère que cela ne va pas à l'encontre des règles du site. Nous deux avons échangé des projets, et il y avait un problème avec le code de l'interrogateur où un retour nul était en train de se produire. Tout d'abord, voici le code corrigé, qui devrait être dans la fonction cropImage():
let ciTL = createVector(createScaledPoint(topLeft, overlay: cameraView, image: image), image: image)
let ciTR = createVector(createScaledPoint(topRight, overlay: cameraView, image: image), image: image)
let ciBR = createVector(createScaledPoint(bottomRight, overlay: cameraView, image: image), image: image)
let ciBL = createVector(createScaledPoint(bottomLeft, overlay: cameraView, image: image), image: image)
Le problème est avec les deux dernières lignes, qui ont été transposés en passant bottomLeft où il aurait dû être bottomRight, et vice-versa . (Facile erreur à faire, je l'ai fait aussi!)
Une explication pour aider ceux qui utilisent CIPerspectiveCorrection (et d'autres filtres qui utilisent CIVectors). (1) Un CIVector peut avoir n'importe où - je pense 2 à, eh bien, une quantité presque infinie de composants. Cela dépend du filtre. Dans ce cas, il y a deux composants (X, Y). Assez simple, mais la torsion est que les 4 CIVectors décrivent 4 points dans l'extension CIImage où l'origine est en bas à gauche, pas en haut à gauche.
(2) Note Je n'ai pas dit une forme à 4 côtés. Vous pouvez réellement avoir une forme de "figure 8" où le point "en bas à droite" est à gauche du point "en bas à gauche"! Cela se traduirait par une forme où deux côtés se croisent.
(3) Tout ce qui compte est que tous les 4 points se trouvent avec l'extension CIImage.Si ce n'est pas le cas, le filtre avec le retour est nul pour son image de sortie. Une dernière note pour ceux qui n'ont pas encore travaillé avec les filtres CIImage - les filtres ne s'exécuteront pas tant que vous n'aurez pas demandé l'image de sortie. Vous pouvez en instancier un, remplir les paramètres, les chaîner, peu importe. Vous pouvez même faire une faute de frappe dans le nom du filtre (ou l'une de leurs clés). Jusqu'à ce que votre code demande le filter.outputImage, rien ne se passe.
Si vous connaissez les coordonnées du CGRect que vous voulez recadrer, utilisez le filtre CIPerspectiveCorrection. Je vais jeter du code dans quelques minutes. – dfd
@dfd J'essaie de voir comment fonctionne CIPerspectiveCorrection, si vous fournissez du code, cela aidera beaucoup! Merci – Marckaraujo
Je viens de l'ajouter. Copier/coller d'une application qui fonctionne dans un nouveau projet et il semble que cela fonctionne - il semble que cela apparaisse à l'envers, ce qui est probablement dû à une certaine transposition CGPoint. Dites-moi, je vais lancer mon application ** très bêta ** dans un GitHub public. – dfd