ios - Recortar la salida de AVCaptureVideoPreviewLayer a un cuadrado
objective-c avcapturesession (4)
Tengo problemas con mi método AVCaptureVideoPreviewLayer al capturar un UIImage recortado de la pantalla visible. Actualmente está funcionando pero no genera el recorte correcto que requiero.
Estoy intentando dar salida a un cuadrado, pero (por su aspecto) parece estar dando toda la altura y comprimiendo la imagen.
La imagen anterior muestra la pantalla EN VIVO y la imagen posterior muestra la imagen una vez que se presiona el botón de captura. Puede ver que se ha cambiado verticalmente para ajustarse al cuadrado, pero la altura no se ha recortado verticalmente.
Capturar código de imagen
[stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
if (imageSampleBuffer != NULL) {
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
[self processImage:[UIImage imageWithData:imageData]];
}
}];
Código de recorte
- (void) processImage:(UIImage *)image { //process captured image, crop, resize and rotate
haveImage = YES;
CGRect deviceScreen = _previewLayer.bounds;
CGFloat width = deviceScreen.size.width;
CGFloat height = deviceScreen.size.height;
NSLog(@"WIDTH %f", width); // Outputing 320
NSLog(@"HEIGHT %f", height); // Outputting 320
UIGraphicsBeginImageContext(CGSizeMake(width, width));
[image drawInRect: CGRectMake(0, 0, width, width)];
UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGRect cropRect = CGRectMake(0, 0, width, width);
CGImageRef imageRef = CGImageCreateWithImageInRect([smallImage CGImage], cropRect);
CGImageRelease(imageRef);
[captureImageGrab setImage:[UIImage imageWithCGImage:imageRef]];
}
¿Has intentado configurar videoGravity?
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
Incluso si la capa de vista previa es cuadrada, tenga en cuenta que la imagen fija generada mantiene su tamaño original.
Por lo que veo, el problema está aquí:
UIGraphicsBeginImageContext(CGSizeMake(width, width));
[image drawInRect: CGRectMake(0, 0, width, width)];
Ya hiciste tu cuadrado de contexto con la primera línea. Aún necesita dibujar su imagen en su formato original, será recortado por ese contexto. En la segunda línea, está forzando el dibujo de la imagen original en un cuadrado, haciendo que se vea "exprimido".
Debería encontrar la altura de imagen correcta que mantenga la proporción original mientras ajusta su "ancho". A continuación, querrá dibujar esa imagen con el tamaño correcto (manteniendo la proporción original) en su contexto cuadrado. Si desea recortar el centro, cambie la posición Y del dibujo.
Algo similar a esto:
- (void) processImage:(UIImage *)image {
UIGraphicsBeginImageContext(CGSizeMake(width, width));
CGFloat imageHeight = floorf(width / image.width * image.height);
CGFloat offsetY = floorf((imageHeight - width) / 2.0f);
[image drawInRect: CGRectMake(0, -offsetY, width, imageHeight)];
UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[captureImageGrab setImage:smallImage];
}
Deberias hacer eso.
La relación de aspecto está distorsionada porque la imagen capturada por la cámara no es cuadrada. Al dibujarlo en un cuadrado, se dibujará con una relación de aspecto distorsionada. Una posible solución es dibujar la imagen en el cuadrado conservando la relación de aspecto como:
- (void) processImage:(UIImage *)image { //process captured image, crop, resize and rotate
haveImage = YES;
CGRect deviceScreen = _previewLayer.bounds;
CGFloat width = deviceScreen.size.width;
CGFloat height = deviceScreen.size.height;
NSLog(@"WIDTH %f", width); // Outputing 320
NSLog(@"HEIGHT %f", height); // Outputting 320
UIGraphicsBeginImageContext(CGSizeMake(width, width));
CGFloat aspect_h = image.size.height * width / image.size.width;
[image drawInRect: CGRectMake(0, -(aspect_h - width)/2.0, width, aspect_h)];
UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGRect cropRect = CGRectMake(0, 0, width, width);
CGImageRef imageRef = CGImageCreateWithImageInRect([smallImage CGImage], cropRect);
CGImageRelease(imageRef);
}
La nueva imagen será de ancho ancho . Para ese ancho, la altura que mantendrá la relación de aspecto es aspect_h . Luego, la imagen se desplaza a lo largo del eje y para centrarla verticalmente.
Tenga en cuenta que si su aplicación funcionara en orientaciones que no son verticales, se necesita un poco más de trabajo.
Otra observación: puede obtener una mejor resolución de imagen si usa image.size.width como el ancho para el nuevo contexto de imagen.
¡espero que esto ayude!
Mirando el código, me parece que estás dibujando la imagen en un cuadrado. Esta es la parte que reduce la imagen sin importar la relación de aspecto.
En su lugar, haz algo como esto:
- (void) processImage:(UIImage *)image { //process captured image, crop, resize and rotate
haveImage = YES;
CGRect deviceScreen = _previewLayer.bounds;
CGFloat width = deviceScreen.size.width;
CGFloat height = deviceScreen.size.height;
NSLog(@"WIDTH %f", width); // Outputing 320
NSLog(@"HEIGHT %f", height); // Outputting 320
CGRect cropRect = CGRectZero;
if (image.size.width > image.size.height) {
cropRect.origin.x = (image.size.width-image.size.height)/2;
cropRect.size = CGSizeMake(image.size.height, image.size.height);
} else {
cropRect.origin.y = (image.size.height-image.size.width)/2;
cropRect.size = CGSizeMake(image.size.width, image.size.width);
}
CGImageRef croppedImage = CGImageCreateWithImageInRect(image.CGImage, cropRect);
UIGraphicsBeginImageContext(CGSizeMake(width, width));
[[UIImage imageWithCGImage:croppedImage] drawInRect:CGRectMake(0, 0, width, width)];
UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGImageRelease(croppedImage);
[captureImageGrab setImage:[UIImage imageWithCGImage:smallImage.CGImage scale:image.scale orientation:image.imageOrientation]];
}