Last active
April 19, 2021 06:02
-
-
Save AllanChen/d1478f6794075b82e9458c13b3539006 to your computer and use it in GitHub Desktop.
The function is the algorithm by yuv nv12 to rgb.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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