Cómo convertir el formato YUV NV21 CVPixelBufferRef al formato RGB de opencv cv::Mat en IOS

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 ~

Para una depuración súper sencilla,
vaya al mercado de aplicaciones

Supongo que te gusta

Origin blog.csdn.net/qq_19313495/article/details/127357984
Recomendado
Clasificación