Как использовать CIFilter с CIPerspectiveCorrection в сеансе захвата? - PullRequest
0 голосов
/ 28 января 2019

Я хочу сканировать документы и исправлять любые проблемы с перспективой телефона, как это делает приложение заметок.Все нормально, пока я не захочу использовать CIFilter(name: "CIPerspectiveCorrection"), затем я испорчу изображение, и я пытаюсь понять, где я иду не так.

Я попытался переключить параметры и другие фильтры или повернутьизображение, но это не сработало для меня.

Вот небольшой проект, который я настроил для проверки всего этого: https://github.com/iViktor/scanner

В основном я запускаю VNDetectRectanglesRequest на AVCaptureSession и сохраняя прямоугольник, который я получаю private var targetRectangle = VNRectangleObservation()

Этот я использую, чтобы пересчитать точки внутри изображения и запустить фильтр на изображении.

extension DocumentScannerViewController: AVCapturePhotoCaptureDelegate {
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
    guard let imageData = photo.fileDataRepresentation()
        else { return }
    guard let ciImage = CIImage(data: imageData, options: [.applyOrientationProperty : true]) else { return }
    let image = UIImage(ciImage: ciImage)

    let imageTopLeft: CGPoint = CGPoint(x: image.size.width * targetRectangle.bottomLeft.x, y: targetRectangle.bottomLeft.y * image.size.height)
    let imageTopRight: CGPoint = CGPoint(x: image.size.width * targetRectangle.bottomRight.x, y: targetRectangle.bottomRight.y * image.size.height)
    let imageBottomLeft: CGPoint = CGPoint(x: image.size.width * targetRectangle.topLeft.x, y: targetRectangle.topLeft.y * image.size.height)
    let imageBottomRight: CGPoint = CGPoint(x: image.size.width * targetRectangle.topRight.x, y: targetRectangle.topRight.y * image.size.height)

    let flattenedImage = image.flattenImage(topLeft: imageTopLeft, topRight: imageTopRight, bottomLeft: imageBottomLeft, bottomRight: imageBottomRight)
    let finalImage = UIImage(ciImage: flattenedImage, scale: image.scale, orientation: image.imageOrientation)

//performSegue(withIdentifier: "showPhoto", sender: image)
//performSegue(withIdentifier: "showPhoto", sender: UIImage(ciImage: flattenedImage))
    performSegue(withIdentifier: "showPhoto", sender: finalImage)

}
}

Этокод, который не работает, и я борюсь с:

extension UIImage {

func flattenImage(topLeft: CGPoint, topRight: CGPoint, bottomLeft: CGPoint, bottomRight: CGPoint) -> CIImage {
    let docImage = self.ciImage!
    let rect = CGRect(origin: CGPoint.zero, size: self.size)
    let perspectiveCorrection = CIFilter(name: "CIPerspectiveCorrection")!
    perspectiveCorrection.setValue(CIVector(cgPoint: self.cartesianForPoint(point: topLeft, extent: rect)), forKey: "inputTopLeft")
    perspectiveCorrection.setValue(CIVector(cgPoint: self.cartesianForPoint(point: topRight, extent: rect)), forKey: "inputTopRight")
    perspectiveCorrection.setValue(CIVector(cgPoint: self.cartesianForPoint(point: bottomLeft, extent: rect)), forKey: "inputBottomLeft")
    perspectiveCorrection.setValue(CIVector(cgPoint: self.cartesianForPoint(point: bottomRight, extent: rect)), forKey: "inputBottomRight")
    perspectiveCorrection.setValue(docImage, forKey: kCIInputImageKey)

    return perspectiveCorrection.outputImage!
}

func cartesianForPoint(point:CGPoint,extent:CGRect) -> CGPoint {
    return CGPoint(x: point.x,y: extent.height - point.y)
}
}

Итак, в конце я хочу отсканировать документ, например счет, и автоматически исправить любую ошибку пользователя, например, проблемы с перспективой.Прямо сейчас фильтр, который я добавляю к изображению, приводит к странному эффекту, похожему на веер.

1 Ответ

0 голосов
/ 29 января 2019

Основываясь на комментариях, я обновил код, в котором я использовал targetRectangle, чтобы вместо этого использовать точки, представленные нарисованным контуром, и изменил место, где я использовал их для изображения, потому что CI использует другую систему координат, а изображение зеркально отражается.

Я обновил

    private func startScanner() {
         ... ... ...
               let request = VNDetectRectanglesRequest { req, error in
                    DispatchQueue.main.async {
                        if let observation = req.results?.first as? VNRectangleObservation {
                            let points = self.targetRectLayer.drawTargetRect(observation: observation, previewLayer: self.previewLayer, animated: false)
                            let size = self.scannerView.frame.size
                            self.trackedTopLeftPoint = CGPoint(x: points.topLeft.x / size.width, y: points.topLeft.y / size.height )
                            self.trackedTopRightPoint = CGPoint(x: points.topRight.x / size.width, y: points.topRight.y / size.height )
                            self.trackedBottomLeftPoint = CGPoint(x: points.bottomLeft.x / size.width, y: points.bottomLeft.y / size.height )
                            self.trackedBottomRightPoint = CGPoint(x: points.bottomRight.x / size.width, y: points.bottomRight.y / size.height )
                        } else {
                            _ = self.targetRectLayer.drawTargetRect(observation: nil, previewLayer: self.previewLayer, animated: false)
                        }
                    }
                }
        }

и

extension DocumentScannerViewController: AVCapturePhotoCaptureDelegate {
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
    guard let imageData = photo.fileDataRepresentation()
        else { return }
    guard let ciImage = CIImage(data: imageData, options: [.applyOrientationProperty : true]) else { return }
    let image = UIImage(ciImage: ciImage)

    // CoreImage is working with cartesian coordinates, basically y:0 is in the bottom left corner
    let imageTopLeft: CGPoint = CGPoint(x: image.size.width * trackedBottomLeftPoint.x, y: trackedBottomLeftPoint.y * image.size.height)
    let imageTopRight: CGPoint = CGPoint(x: image.size.width * trackedTopLeftPoint.x, y: trackedTopLeftPoint.y * image.size.height)
    let imageBottomLeft: CGPoint = CGPoint(x: image.size.width * trackedBottomRightPoint.x, y: trackedBottomRightPoint.y * image.size.height)
    let imageBottomRight: CGPoint = CGPoint(x: image.size.width * trackedTopRightPoint.x, y: trackedTopRightPoint.y * image.size.height)

    let flattenedImage = image.flattenImage(topLeft: imageTopLeft, topRight: imageTopRight, bottomLeft: imageBottomLeft, bottomRight: imageBottomRight)
    let newCGImage = CIContext(options: nil).createCGImage(flattenedImage, from: flattenedImage.extent)
    let doneCroppedImage = UIImage(cgImage: newCGImage!, scale: image.scale, orientation: image.imageOrientation)
    performSegue(withIdentifier: "showPhoto", sender: doneCroppedImage)
}
}

Это исправило.

...