prefacio
Debido a las necesidades comerciales, necesitamos realizar dicha conversión. Actualmente hay dos métodos de conversión escritos.
Cuando se ejecuta en una máquina iPhoneX real, un método requiere 24 ms para un cuadro y el uso de la CPU es del 85 %, y el otro método requiere 17 ms para un cuadro y el uso de la CPU es del 140 %. Hablemos de ello en detalle a continuación.
método uno
La idea de conversión es la ruta de CVPixelBufferRef->UIImage->cv::Mat.
Directamente sobre el método:
Primero, el método de CVPixelBufferRef->UIImage
- (UIImage*)uiImageFromPixelBuffer:(CVPixelBufferRef)p {
CIImage* ciImage = [CIImage imageWithCVPixelBuffer : p];
CIContext* context = [CIContext contextWithOptions : @{kCIContextUseSoftwareRenderer : @(YES)}];
CGRect rect = CGRectMake(0, 0, CVPixelBufferGetWidth(p), CVPixelBufferGetHeight(p));
CGImageRef videoImage = [context createCGImage : ciImage fromRect : rect];
UIImage* image = [UIImage imageWithCGImage : videoImage];
CGImageRelease(videoImage);
return image;
}
Luego está el método de UIImage->cv::Mat
- (cv::Mat)cvMatFromUIImage:(UIImage *)image
{
CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);
CGFloat cols = image.size.width;
CGFloat rows = image.size.height;
cv::Mat cvMat(rows, cols, CV_8UC4); // 8 bits per component, 4 channels (color channels + alpha)
CGContextRef contextRef = CGBitmapContextCreate(cvMat.data, // Pointer to data
cols, // Width of bitmap
rows, // Height of bitmap
8, // Bits per component
cvMat.step[0], // Bytes per row
colorSpace, // Colorspace
kCGImageAlphaNoneSkipLast |
kCGBitmapByteOrderDefault); // Bitmap info flags
CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage);
CGContextRelease(contextRef);
return cvMat;
}
Al llamar a estos dos métodos, debe agregar un grupo de liberación automática; de lo contrario, la memoria explotará.
Esta ruta de conversión es lenta pero el uso de CPU no es alto.
Método dos
La idea de conversión es CVPixelBufferRef->YUV cv::Mat->RGB cv::Mat
primer método
- (cv::Mat)cvMatFromPixelBuffer:(CVPixelBufferRef)p
{
CVPixelBufferLockBaseAddress(p, 0);
unsigned char *base = (unsigned char *)CVPixelBufferGetBaseAddress(p);
uint8_t *src_y = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(p, 0);
uint8_t *src_uv =(unsigned char *) CVPixelBufferGetBaseAddressOfPlane(p, 1);
int height = (int)CVPixelBufferGetHeight( p );
int width = (int)CVPixelBufferGetWidth(p);
cv::Mat yuvimg(height*3/2, width, CV_8UC1);
for(int i=0; i<height*width; i++){
*(yuvimg.data + i) = *(src_y + i);
}
int delta_h = height*3/2 - height;
for(int i=0; i<delta_h; i++){
for(int j=0; j<width; j++){
*(yuvimg.data + (height + i)*width + j) = *(src_uv + i*width + j);
}
}
//writeImage2Document("testim.jpg", yuvimg);
cv::Mat rgbimg(height, width, CV_8UC4);
cv::cvtColor(yuvimg, rgbimg, cv::COLOR_YUV2RGBA_NV21);
//CVPixelBufferRef testimg = [self getImageBufferFromMat:rgbimg];
CVPixelBufferUnlockBaseAddress( p, 0 );
return rgbimg;
}
Este método utiliza directamente opencv para convertir, que será más rápido, pero el uso de la CPU será alto.
Además, cabe señalar que los datos YUV deben escribirse en cv::Mat por separado de Y y UV, en lugar de obtener directamente la dirección de CVPixelBufferGetBaseAddress. No sé por qué, si toma directamente la dirección de CVPixelBufferGetBaseAddress, habrá un código confuso en la dirección inicial, lo que hará que toda la pantalla se mueva hacia la derecha.
llámalo un día ~