Качество изображения влияет на масштабирование изображения с помощью vImageScale_ARGB8888 - Cocoa Objective C - PullRequest
0 голосов
/ 01 августа 2020

Я снимаю экран моей системы с помощью AVCaptureSession, а затем создаю видеофайл из захваченных буферов изображений. Он отлично работает.

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

- (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {

    CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    
    if (pixelBuffer == NULL) { return; }
    
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    
    size_t finalWidth = 1080;
    size_t finalHeight = 720;
    
    size_t sourceWidth = CVPixelBufferGetWidth(imageBuffer);
    size_t sourceHeight = CVPixelBufferGetHeight(imageBuffer);
    
    CGRect aspectRect = AVMakeRectWithAspectRatioInsideRect(CGSizeMake(sourceWidth, sourceHeight), CGRectMake(0, 0, finalWidth, finalHeight));
    
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);

    size_t startY = aspectRect.origin.y;
    size_t yOffSet = (finalWidth*startY*4);
    CVPixelBufferLockBaseAddress(imageBuffer, 0);
    
    void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);
    
    void* destData = malloc(finalHeight * finalWidth * 4);
    
    vImage_Buffer srcBuffer = { (void *)baseAddress, sourceHeight, sourceWidth, bytesPerRow};
    vImage_Buffer destBuffer = { (void *)destData+yOffSet, aspectRect.size.height, aspectRect.size.width, aspectRect.size.width * 4};
    
    vImage_Error err = vImageScale_ARGB8888(&srcBuffer, &destBuffer, NULL, 0);
    
    CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
    OSType pixelFormat = CVPixelBufferGetPixelFormatType(imageBuffer);
    
    CVImageBufferRef pixelBuffer1 = NULL;
    
    CVReturn result = CVPixelBufferCreateWithBytes(NULL, finalWidth, finalHeight, pixelFormat, destData, finalWidth * 4, NULL, NULL, NULL, &pixelBuffer1);
}

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

Это нормально работает, если я изменяю формат выходных пикселей на RGB с помощью кода ниже.

output.videoSettings = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], (id)kCVPixelBufferPixelFormatTypeKey, nil];

Но мне нужны буферы изображений в формате YUV ( формат по умолчанию для AVCaptureVideoDataOutput), поскольку это уменьшит размер буфера при передаче по сети.

Изображение после масштабирования:

enter image description here

Image resized with Preview application:

введите описание изображения здесь

Я пробовал использовать vImageScale_CbCr8 вместо vImageScale_ARGB8888, но полученное изображение не содержало правильных значений RGB.

Я также заметил, что есть функция для преобразования формата изображения : vImageConvert_422YpCbYpCr8ToARGB8888(const vImage_Buffer *src, const vImage_Buffer *dest, const vImage_YpCbCrToARGB *info, const uint8_t permuteMap[4], const uint8_t alpha, vImage_Flags flags);

Но я не знаю, какими должны быть значения для vImage_YpCbCrToARGB и permuteMap, так как я ничего не знаю об обработке изображений.

Ожидаемое решение:

Как преобразовать буферы пикселей YUV в буферы RGB и обратно в YUV (или) Как масштабировать буферы пикселей YUV, не влияя на значения RGB.

Ответы [ 2 ]

1 голос
/ 04 августа 2020

Это отсутствующие форматы пикселей YpCbCr в ARGB и наоборот для вашего преобразования.

vImage_Error err = kvImageNoError;

//static vImage_ARGBToYpCbCr inInfo;
static vImage_YpCbCrToARGB outInfo;
static bool infoGenerated = false;
if (!infoGenerated) {
    vImage_YpCbCrPixelRange pixelRange;
    pixelRange.Yp_bias         =   16; // encoding for Y' = 0.0
    pixelRange.CbCr_bias       =  128; // encoding for CbCr = 0.0
    pixelRange.YpRangeMax      =  235; // encoding for Y'= 1.0
    pixelRange.CbCrRangeMax    =  240; // encoding for CbCr = 0.5
    pixelRange.YpMax           =  255; // a clamping limit above which the value is not allowed to go. 255 is fastest. Use pixelRange.YpRangeMax if you don't want Y' > 1.
    pixelRange.YpMin           =    0; // a clamping limit below which the value is not allowed to go. 0 is fastest. Use pixelRange.Yp_bias if you don't want Y' < 0.
    pixelRange.CbCrMax         =  255; // a clamping limit above which the value is not allowed to go. 255 is fastest.  Use pixelRange.CbCrRangeMax, if you don't want CbCr > 0.5
    pixelRange.CbCrMin         =    0; // a clamping limit above which the value is not allowed to go. 0 is fastest.  Use (2*pixelRange.CbCr_bias - pixelRange.CbCrRangeMax), if you don't want CbCr < -0.5
    // ( pixelRange.CbCr_bias - (pixelRange.CbCrRangeMax - pixelRange.CbCr_bias) = 2*pixelRange.CbCr_bias - pixelRange.CbCrRangeMax )
    
    const vImage_YpCbCrToARGBMatrix *matrixYpCbCrToARGB = kvImage_YpCbCrToARGBMatrix_ITU_R_601_4;
    err = vImageConvert_YpCbCrToARGB_GenerateConversion(matrixYpCbCrToARGB, &pixelRange, &outInfo, kvImage422YpCbYpCr8, kvImageARGB8888, kvImageNoFlags);
    
    //const vImage_ARGBToYpCbCrMatrix *matrixARGBToYpCbCr = kvImage_ARGBToYpCbCrMatrix_ITU_R_601_4;
    //err = vImageConvert_ARGBToYpCbCr_GenerateConversion(matrixARGBToYpCbCr, &pixelRange,  &inInfo, kvImageARGB8888, kvImage422YpCbYpCr8, kvImageNoFlags);
    
    if (err!=kvImageNoError) infoGenerated = true;
}

if (infoGenerated) {
    //permuteMap, Values that can be used to switch the channel order of destination.
    const uint8_t permuteMap[4] = {0, 1, 2, 3}; // or NULL are ARGB8888.
    //const uint8_t permuteMap[4] = {3, 2, 1, 0}; // is BGRA8888.
    // Any order of permuteMap is allowed when each permuteMap value is 0, 1, 2, or 3.

    err = vImageConvert_422YpCbYpCr8ToARGB8888(&srcBuffer, &destBuffer, &outInfo, permuteMap, 0, kvImageNoFlags);
    if (err!=kvImageNoError) NSLog(@"vImage_Error converting 422YpCbYpCr8ToARGB8888 errorcode:%ld",err);
    
    //err = vImageConvert_ARGB8888To422YpCbYpCr8(&srcBuffer, &destBuffer, &inInfo, permuteMap, kvImageNoError);
    //if (err!=kvImageNoError) NSLog(@"vImage_Error converting ARGB8888To422YpCbYpCr8 errorcode:%ld",err);
}
0 голосов
/ 08 августа 2020

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

CVPixelBufferRef imageBuffer;
CVPixelBufferCreate(kCFAllocatorDefault, sourceWidth, sourceHeight, kCVPixelFormatType_32ARGB, 0, &imageBuffer);

VTPixelTransferSessionTransferImage(pixelTransferSession, pixelBuffer, imageBuffer);
...