Skip to content

Instantly share code, notes, and snippets.

@AllanChen
Last active April 19, 2021 06:02
Show Gist options
  • Save AllanChen/d1478f6794075b82e9458c13b3539006 to your computer and use it in GitHub Desktop.
Save AllanChen/d1478f6794075b82e9458c13b3539006 to your computer and use it in GitHub Desktop.
The function is the algorithm by yuv nv12 to rgb.
/*
* How to use this funtion
*/
- (void)videoCaptureCallback:(CVPixelBufferRef)pixelBuffer{
// [self test_yuv_to_rgb:pixelBuffer];
CVPixelBufferLockBaseAddress(pixelBuffer, 0);
int width = (int)CVPixelBufferGetWidth(pixelBuffer);
int height = (int)CVPixelBufferGetHeight(pixelBuffer);
unsigned char *yBuffer = (unsigned char*)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0); //ybuffer
size_t yPitch = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
unsigned char *cbCrBuffer = (unsigned char*)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1);// uvbuffer
size_t cbCrPitch = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1);
unsigned char *ptr_yuv_data = new unsigned char [yPitch * height + (cbCrPitch * (height/2))];
//combine y and uv buffer
memcpy(ptr_yuv_data, yBuffer, yPitch * height);
memcpy(ptr_yuv_data + yPitch * height , cbCrBuffer, cbCrPitch * height/2 );
//malloc rgba buffer
unsigned char *rgbaBuffer =(unsigned char*)malloc(width * height * 4);
//process the buffer
yuv_to_rgb24(rgbaBuffer, ptr_yuv_data, width, height, yPitch, true);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(rgbaBuffer, width, height, 8, width * 4, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast);
CGImageRef quartzImage = CGBitmapContextCreateImage(context);
UIImage *image = [UIImage imageWithCGImage:quartzImage];
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
CGImageRelease(quartzImage);
free(rgbaBuffer);
}
void yuv_to_rgb24(unsigned char rgb[], unsigned char yuv[], int width, int height, size_t prerow, bool isNV12 = true) {
int total = (int)prerow * height;
int16_t Y, U, V;
int16_t R, G, B;
int index = 0;
for (int h = 0; h < height; h++) {
unsigned char *yBufferLine = &yuv[h * prerow];
unsigned char *uvdataLine = &yuv[total + (h >> 1) * (int)prerow];
for (int w = 0; w < width; w++) {
Y = yBufferLine[w];
if(isNV12){
U = uvdataLine[w & ~1] ;
V = uvdataLine[w | 1];
}else{
V = uvdataLine[w & ~1] ;
U = uvdataLine[w | 1];
}
R = Y + 1.400*(V-128);
G = Y - 0.343*(U-128) - 0.711*(V-128);
B = Y + 1.765*(U-128);
if (R < 0) R = 0; else if (R > 255) R = 255;
if (G < 0) G = 0; else if (G > 255) G = 255;
if (B < 0) B = 0; else if (B > 255) B = 255;
rgb[index++] = 0xff;
rgb[index++] = B;
rgb[index++] = G;
rgb[index++] = R;
}
}
}
//GPU
uint8_t* convertYCbCrToRGBA (
size_t width,
size_t height,
const uint8_t* yData,
const uint8_t* cbcrData,
uint8_t* rgbaData,
uint8_t alpha,
size_t yBytesPerRow,
size_t cbCrBytesPerRow,
size_t rgbaBytesPerRow
)
{
assert(width <= rgbaBytesPerRow);
// Input RGBA buffer:
vImage_Buffer rgbaBuffer
{
.data = (void*)rgbaData,
.width = (size_t)width,
.height = (size_t)height,
.rowBytes = rgbaBytesPerRow
};
// Destination Y, CbCr buffers:
vImage_Buffer cbCrBuffer
{
.data = (void*)cbcrData,
.width = (size_t)width/2,
.height = (size_t)height/2,
.rowBytes = (size_t)cbCrBytesPerRow // 2 bytes per pixel (Cb+Cr)
};
vImage_Buffer yBuffer
{
.data = (void*)yData,
.width = (size_t)width,
.height = (size_t)height,
.rowBytes = (size_t)yBytesPerRow
};
vImage_Error error = kvImageNoError;
// Conversion information:
static vImage_YpCbCrToARGB info;
{
static bool infoGenerated = false;
if(!infoGenerated)
{
vImage_Flags flags = kvImageNoFlags;
vImage_YpCbCrPixelRange pixelRange
{
.Yp_bias = 0,
.CbCr_bias = 128,
.YpRangeMax = 255,
.CbCrRangeMax = 255,
.YpMax = 255,
.YpMin = 0,
.CbCrMax= 255,
.CbCrMin = 1
};
const vImage_YpCbCrToARGBMatrix * tmat_601_4 = kvImage_YpCbCrToARGBMatrix_ITU_R_601_4;
const vImage_YpCbCrToARGBMatrix * tmat_709_2 = kvImage_YpCbCrToARGBMatrix_ITU_R_709_2; // gpu used
error = vImageConvert_YpCbCrToARGB_GenerateConversion(
tmat_601_4,
&pixelRange,
&info,
kvImage420Yp8_CbCr8,
kvImageARGB8888,
flags
);
if (kvImageNoError != error)
infoGenerated = true;
}
}
static const uint8_t permuteMapRGBA [4] { 1, 2, 3, 0 };
error = vImageConvert_420Yp8_CbCr8ToARGB8888(
&yBuffer,
&cbCrBuffer,
&rgbaBuffer,
&info,
permuteMapRGBA,
255,
kvImageNoFlags | kvImageDoNotTile // Disable multithreading.
);
return (uint8_t *)rgbaBuffer.data;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment