前言:
本文承接:
及其他两篇
接着讲述选择区域相关的处理
旋转,选择区域,对选择的区域做透视校正的滤镜处理
为了简化问题,
项目设置为,仅 iPhone 竖屏
运行在 iPad 上面
这是拍照后,经历了一次左旋的图片
问题
使用滤镜(透视校正)简单,旋转后的图片,很可能与旋转前的图片朝向不一致,这影响了滤镜的效果
场景
ipad 拍照,默认横屏向上,跑 iPhone 竖屏的应用,
相机设置为
connection?.videoOrientation = AVCaptureVideoOrientation.portrait
session.sessionPreset = .hd1280x720
拍出来的照片,朝向 pic.imageOrientation = .right
size 是
- width : 720.0
- height : 1280.0
为了简化计算
选择区域 crop view 的 frame = image view 的 frame
crop view 上面的四个角落点的坐标,基于 crop view 的 bounds,
即 crop view 上面的四个角落点的坐标,基于 CGRect( CGPoint.zero, image view 的 frame.size )
需要,旋转后的 image 朝向始终为向上
拿到的图片朝向,向右。滤镜使用的是正常的开发坐标,结果比较搞。
实现
简单的滤镜代码
// 第一个参数,图片视图的 bounds,用 size
// 第二个参数,待处理的图片
// 第三个参数,四个坐标
static public func cropImage(in rect: CGRect,with img: UIImage, quad corners: [CGPoint]) throws -> UIImage {
let imgSize = img.size
// 找出图片视图的 bounds 内,图片 aspect ratio 的 frame
let f = AVMakeRect(aspectRatio: imgSize, insideRect: rect)
// 做一个放大,当前视图内的坐标,方法到图片对应的坐标
let quad = corners.map { (pt) -> CGPoint in
return pt.inner(img: imgSize, relative: f.size)
}
let ciImage = CIImage(image: img)
// print("rect: ", rect, "corners: ",corners)
let perspectiveCorrection = CIFilter(name: "CIPerspectiveCorrection")
// 保证四个坐标是,左上,右上,右下,左下
let orderedQuad = try orderPointsInQuadrangle(quad: quad)
// 下面是滤镜的标准流程
let context = CIContext(options: nil)
// print("ordered quad: ", orderedQuad, "imgSize: ", imgSize)
guard let transform = perspectiveCorrection else {
throw SECropError.unknown
}
transform.setValue(CIVector(cgPoint: orderedQuad[0].cartesian(for: imgSize)),
forKey: "inputTopLeft")
transform.setValue(CIVector(cgPoint: orderedQuad[1].cartesian(for: imgSize)),
forKey: "inputTopRight")
transform.setValue(CIVector(cgPoint: orderedQuad[2].cartesian(for: imgSize)),
forKey: "inputBottomRight")
transform.setValue(CIVector(cgPoint: orderedQuad[3].cartesian(for: imgSize)),
forKey: "inputBottomLeft")
transform.setValue(ciImage, forKey: kCIInputImageKey)
guard let perspectiveCorrectedImg = transform.outputImage, let cgImage = context.createCGImage(perspectiveCorrectedImg, from: perspectiveCorrectedImg.extent) else {
throw SECropError.unknown
}
return UIImage(cgImage: cgImage, scale: img.scale, orientation: img.imageOrientation)
}
滤镜使用错误如下:
除了朝向问题,
-
还有坐标顺序不对,必须是左上 -> 右上 -> 右下 -> 左下
-
坐标放大错误,太大了,超出了图片的区域
拍照后,图片处理
func picTaken(img imgData: Data){
if let pic = UIImage(data: imgData){
if pic.imageOrientation == .right{
// 文中 ipad 的情况,先改朝向,再翻转
takedImage = pic.up.image(rotated: 1)
}
else{
takedImage = pic
}
}
picCommon()
}
修改朝向
extension UIImage{
var up: UIImage{
image(orientation: .up)
}
func image(orientation orient: UIImage.Orientation) -> UIImage{
if let cg = cgImage{
return UIImage(cgImage: cg, scale: scale, orientation: orient)
}
else{
return self
}
}
}
旋转代码,也挺简单,
没有做 fix orientation,
具体见 github repo
滤镜前,图片预处理
let idx = Int(angleX)
guard let corners = cropView.cornerLocations, let img = takedImage?.image(rotated: idx) else { return }
// 点击旋转按钮,没有旋转图片,只记录旋转图片的次数, 一次 0.5 个 π
// 点击旋转按钮, ImageView 做 rotate tranform , 此时不直接影响 bounds
// 左右旋,需要对 size 翻转
var b = takedImageView.bounds
if idx % 2 != 0{
let s = b.size.flip
b.size = s
}
let croppedImage = try SEQuadrangleHelper.cropImage(in: b, with: img, quad: corners)
滤镜后,修改图片朝向
var final = croppedImage
// 如果 idx 是负数, idx % 4 是 负数
// 然后 idx % 4 + 4 ,必然为正数
let k = (idx % 4 + 4) % 4
switch k {
case 1:
// 修改图片朝向代码,与上面的类似,具体见 github repo
final = croppedImage.left
case 2:
final = croppedImage.down
case 3:
final = croppedImage.right
default:
()
}
takedImageView.image = final
补充,点击旋转按钮
@objc
func rotateBtnClickX(){
angleX -= 1
// 看到的效果,就是调仿射变换
takedImageView.transform = CGAffineTransform(rotationAngle: ImgSingleAngle.time * angleX)
let idx = Int(angleX)
// 图片竖着,有一个 frame
var inS = areaInner
if idx % 2 != 0{
// 图片横着,有一个不同的 frame
inS = areaInnerRotateLeft
}
takedImageView.frame = inS
cropView.refresh(frame: inS)
}