Я занимаюсь хобби-разработчиком и делаю приложение для своей работы. Мне нужно обрезать обнаруженный объект (этикетку продукта) и отправить его в другое представление (чтобы позже применить ocr). Теперь я загрузил пример яблока, на котором отображается желтый прямоугольник с закругленными углами, я заменяю модель на созданную мной и успешно распознаю изображение (бумажная этикетка). Теперь я хочу отобразить его в быстром просмотре. Это мой код
Swift UI uiviewcontrollerpresentable struct
struct CameraCont:UIViewControllerRepresentable {
@Binding var croppedImage:UIImage?
func makeCoordinator() -> Coordinator {
return Coordinator(croppedImage: $croppedImage)
}
class Coordinator:NSObject, SendImageToSwiftUIDelegate {
@Binding var croppedImage:UIImage!
init(croppedImage:Binding<UIImage?>) {
_croppedImage = croppedImage
}
func sendImage(image: UIImage) {
croppedImage = image
print("Delegate called")
}
}
typealias UIViewControllerType = VisionObjectRecognitionViewController
func makeUIViewController(context: Context) -> VisionObjectRecognitionViewController {
let vision = VisionObjectRecognitionViewController()
vision.sendImageDelegate = context.coordinator
return vision
}
func updateUIViewController(_ uiViewController: VisionObjectRecognitionViewController, context: Context) {
}
}
Теперь я создал протокол для связи с uiviewpresentable, и его делегат успешно вызывается. Это мой подкласс контроллера представления.
protocol SendImageToSwiftUIDelegate {
func sendImage(image:UIImage)
}
class VisionObjectRecognitionViewController: ViewController {
var sendImageDelegate:SendImageToSwiftUIDelegate!
private var detectionOverlay: CALayer! = nil
private var observationWidthBiggherThan180 = false
private var rectToCrop = CGRect()
// Vision parts
private var requests = [VNRequest]()
@discardableResult
func setupVision() -> NSError? {
// Setup Vision parts
let error: NSError! = nil
guard let modelURL = Bundle.main.url(forResource: "exampleModelFP16", withExtension: "mlmodelc") else {
return NSError(domain: "VisionObjectRecognitionViewController", code: -1, userInfo: [NSLocalizedDescriptionKey: "Model file is missing"])
}
do {
let visionModel = try VNCoreMLModel(for: MLModel(contentsOf: modelURL))
let objectRecognition = VNCoreMLRequest(model: visionModel, completionHandler: { (request, error) in
DispatchQueue.main.async(execute: {
// perform all the UI updates on the main queue
if let results = request.results {
self.drawVisionRequestResults(results)
}
})
})
self.requests = [objectRecognition]
} catch let error as NSError {
print("Model loading went wrong: \(error)")
}
return error
}
func drawVisionRequestResults(_ results: [Any]) {
CATransaction.begin()
CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
detectionOverlay.sublayers = nil // remove all the old recognized objects
for observation in results where observation is VNRecognizedObjectObservation {
guard let objectObservation = observation as? VNRecognizedObjectObservation else {
continue
}
// Select only the label with the highest confidence.
let topLabelObservation = objectObservation.labels[0]
let objectBounds = VNImageRectForNormalizedRect(objectObservation.boundingBox, Int(bufferSize.width), Int(bufferSize.height))
let shapeLayer = self.createRoundedRectLayerWithBounds(objectBounds)
let textLayer = self.createTextSubLayerInBounds(objectBounds,
identifier: topLabelObservation.identifier,
confidence: topLabelObservation.confidence)
shapeLayer.addSublayer(textLayer)
detectionOverlay.addSublayer(shapeLayer)
if shapeLayer.bounds.size.width > 180 {
// perform crop and apply perspective filter
self.observationWidthBiggherThan180 = true
self.rectToCrop = shapeLayer.bounds
} else {
self.observationWidthBiggherThan180 = false
}
}
self.updateLayerGeometry()
CATransaction.commit()
}
// MARK: - Function to capure the output of the camera and perform the image recognition
override func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
return
}
if observationWidthBiggherThan180 {
let imageToCrop = CIImage(cvPixelBuffer: pixelBuffer)
let perspectiveTransform = CIFilter(name: "CIPerspectiveTransform")
sendImageDelegate.sendImage(image: UIImage(ciImage: imageToCrop))
print(imageToCrop)
}
let exifOrientation = exifOrientationFromDeviceOrientation()
let imageRequestHandler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer, orientation: exifOrientation, options: [:])
do {
try imageRequestHandler.perform(self.requests)
} catch {
print(error)
}
}
override func setupAVCapture() {
super.setupAVCapture()
// setup Vision parts
setupLayers()
updateLayerGeometry()
setupVision()
// start the capture
startCaptureSession()
}
func setupLayers() {
detectionOverlay = CALayer() // container layer that has all the renderings of the observations
detectionOverlay.name = "DetectionOverlay"
detectionOverlay.bounds = CGRect(x: 0.0,
y: 0.0,
width: bufferSize.width,
height: bufferSize.height)
detectionOverlay.position = CGPoint(x: rootLayer.bounds.midX, y: rootLayer.bounds.midY)
rootLayer.addSublayer(detectionOverlay)
}
func updateLayerGeometry() {
let bounds = rootLayer.bounds
var scale: CGFloat
let xScale: CGFloat = bounds.size.width / bufferSize.height
let yScale: CGFloat = bounds.size.height / bufferSize.width
scale = fmax(xScale, yScale)
if scale.isInfinite {
scale = 1.0
}
CATransaction.begin()
CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
// rotate the layer into screen orientation and scale and mirror
detectionOverlay.setAffineTransform(CGAffineTransform(rotationAngle: CGFloat(.pi / 2.0)).scaledBy(x: scale, y: -scale))
// center the layer
detectionOverlay.position = CGPoint(x: bounds.midX, y: bounds.midY)
CATransaction.commit()
}
func createTextSubLayerInBounds(_ bounds: CGRect, identifier: String, confidence: VNConfidence) -> CATextLayer {
let textLayer = CATextLayer()
textLayer.name = "Object Label"
let formattedString = NSMutableAttributedString(string: String(format: "\(identifier)\nConfidence: %.2f", confidence))
let largeFont = UIFont(name: "Helvetica", size: 24.0)!
formattedString.addAttributes([NSAttributedString.Key.font: largeFont], range: NSRange(location: 0, length: identifier.count))
textLayer.string = formattedString
textLayer.bounds = CGRect(x: 0, y: 0, width: bounds.size.height - 10, height: bounds.size.width - 10)
textLayer.position = CGPoint(x: bounds.midX, y: bounds.midY)
textLayer.shadowOpacity = 0.7
textLayer.shadowOffset = CGSize(width: 2, height: 2)
textLayer.foregroundColor = CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [0.0, 0.0, 0.0, 1.0])
textLayer.contentsScale = 2.0 // retina rendering
// rotate the layer into screen orientation and scale and mirror
textLayer.setAffineTransform(CGAffineTransform(rotationAngle: CGFloat(.pi / 2.0)).scaledBy(x: 1.0, y: -1.0))
return textLayer
}
func createRoundedRectLayerWithBounds(_ bounds: CGRect) -> CALayer {
let shapeLayer = CALayer()
shapeLayer.bounds = bounds
shapeLayer.position = CGPoint(x: bounds.midX, y: bounds.midY)
shapeLayer.name = "Found Object"
shapeLayer.backgroundColor = CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [1.0, 1.0, 0.2, 0.4])
shapeLayer.cornerRadius = 7
return shapeLayer
}
}
Если размер представления больше 180 пикселей (телефон рядом с этикеткой). Проблема в том, что я не могу отобразить все, что я обрезал из буфера пикселей. Когда я закрываю свое отображение, отображается пустое пространство и не отображается никаких изображений.
struct ContentView: View {
@State private var image:Image?
@State private var showingCamera = false
@State private var inputImage:UIImage?
var body: some View {
VStack {
image?
.resizable()
.scaledToFit()
Button("Show camera") {
self.showingCamera = true
}
}
.sheet(isPresented: $showingCamera, onDismiss: loadImage) {
CameraCont(croppedImage: self.$inputImage)
.edgesIgnoringSafeArea(.top)
}
}
func loadImage() {
guard let inputImage = inputImage else {return}
image = Image(uiImage: inputImage)
}
}
Если кто-то знает, в чем я ошибаюсь, дайте мне подсказку. Я думаю, проблема в том, когда я создаю изображение
let imageToCrop = CIImage (cvPixelBuffer: pixelBuffer) Но я не знаю решения.
Большое спасибо.
Также мне нужно Чтобы узнать об AVFoundation и преобразовании координат, может ли кто-нибудь порекомендовать мне хорошее место для его изучения?