欢迎来到电脑知识学习网,专业的电脑知识大全学习平台!

手机版

雷神str2win10-(雷神加速器)

视频教程 发布时间:2022-12-15 18:39:30
(雷神加速器)

这段时间学习音视频,在网上找了很多资料,翻看到雷神的博客,说的很是详细,准备按照雷神博客走一遍。感谢前人的栽树。


本文记录RGB/YUV视频像素数据的处理方法,不使用任何第三方库,完全用C实现。视频像素数据在视频播放器的解码流程中的位置如下图所示,RGB/YUV查看用yuvplayer.exe,已上传到项目git中。


包括如下几个处理函数:

分离YUV420P像素数据中的Y、U、V分量分离YUV444P像素数据中的Y、U、V分量将YUV420P像素数据去掉颜色,变成灰度图将YUV420P像素数据的亮度电脑减半将YUV420P像素数据的周围加上边框生成YUV420P格式的灰阶测试图计算两个YUV420P像素数据的PSNR分离RGB24像素数据中的R、G、B分量将RGB24格式像素数据封装为BMP图像将RGB24格式像素数据转换为YUV420P格式像素数据生成RGB24格式的彩条测试图


1) 分离YUV420P像素数据中的Y、U、V分量

/** * Split Y, U, V planes in YUV420P file. * @param jstr_url Location of Input YUV file. * @param w Width of Input YUV file. * @param h Height of Input YUV file. * @param num Number of frames to process. * 分离YUV420P像素数据中的Y、U、V分量 * 如果视频帧的宽和高分别为w和h,那么一帧YUV420P像素数据一共占用w*h*3/2 Byte的数据。其中前w*h Byte存储Y,接着的w*h*1/4 Byte存储U,最后w*h*1/4 Byte存储V */extern "C" JNIEXPORT jint JNICALLJava_com_xohn_ffmpeg_AVUtils_yuv420Split( JNIEnv *env,jobject /* this */, jstring jstr_url, jint w, jint h, jint num) { char input_url[100] = {0}; const char *str = env->GetStringUTFChars(jstr_url, NULL); sprintf(input_url,"%s",str); env->ReleaseStringUTFChars(jstr_url,str); FILE *fp = fopen(input_url,&电脑#34;rb+"); if(fp == NULL) return -1; strcpy(input_url+strlen(input_url)-4,"_y.y"); FILE *fp1 = fopen(input_url,"wb+"); strcpy(input_url+strlen(input_url)-4,"_u.y"); FILE *fp2 = fopen(input_url,"wb+"); strcpy(input_url+strlen(input_url)-4,"_v.y"); FILE *fp3 = fopen(input_url,"wb+"); unsigned char *pic = (unsigned char *)malloc(w*h*3/2); for(int i = 0;i < num;i++) { fread(pic, 1, w * h * 3 / 2, fp); //Y fwrite(pic, 1, w * h, fp1); //U fwrite(pic + w * h, 1, w * h / 4, fp2); //V fwrite(pic + w * h * 5 / 4, 1, w * h / 4, fp3); } free(pic); fclose(fp); fclose(fp1); fclose(fp2); fclose(fp3); return 0;}

函数调用

AVUtils.getInstance().yuv420Split(ROOT+"lena_256x256_yuv420p.yuv",256,256,1);

运行上面代码后会在同目录下生成y、u、v三个文件,工具查看要改为对应格式和分辨率大小查看

lena_256x256_yuv420p_y.y: Y数据, 分辨率为256x256lena_256x256_yuv420p_u.y: U数据, 分辨率为128x128lena_256x256_yuv420p_v.y: V数据, 分辨率为128x128

原始图片:

lena_256x256_yuv420p.yuv

生成的y、u、v分量图片如下:

lena_256x256_yuv420p_y.y

lena_256x256_yuv420p_u.y

lena_256x256_yuv420p_v.y


2) 分离YUV444P像素数据中的Y、U、V分量

/** * Split Y, U, V planes in YUV444P file. * @param jstr_url Location of YUV file. * @param w Width of Input YUV file. * @param h Height of Input YUV file. * @param num Number of frames to process. * 分离YUV444P像素数据中的Y、U、V分量 * 如果视频帧的宽和高分别为w和h,那么一帧YUV444P像素数据一共占用w*h*3 Byte的数据。其中前w*h Byte存储Y,接着的w*h Byte存储U,最后w*h Byte存储V */extern "C" JNIEXPORT jint JNICALLJava_com_xohn_ffmpeg_AVUtils_yuv444Split( JNIEnv *env,jobject /* this */, jstring jstr_url, jint w, jint h, jint num) { char input_url[100] = {0}; const char *str = env->GetStringUTFChars(jstr_url, NULL); sprintf(input_url,"%s",str); env->ReleaseStringUTFChars(jstr_url,str); FILE *fp = fopen(input_url,"rb+"); if(fp == NULL) return -1; strcpy(input_url+strlen(input_url)-4,"_y.y"); FILE *fp1 = fopen(input_url,"wb+"); strcpy(input_url+strlen(input_url)-4,"_u.y"); FILE *fp2 = fopen(input_url,"wb+"); strcpy(input_url+strlen(input_url)-4,"_v.y"); FILE *fp3 = fopen(input_url,"wb+"); unsigned char *pic = (unsigned char *) malloc(w * h * 3); for(int i = 0;i < num;i++) { fread(pic, 1, w * h * 3, fp); //Y fwrite(pic, 1, w * h, fp1); //U fwrite(pic + w * h, 1, w * h, fp2); //V fwrite(pic + w * h * 2, 1, w * h, fp3); } free(pic); fclose(fp); fclose(fp1); fclose(fp2); fclose(fp3); return 0;}

函数调用

AVUtils.getInstance().yuv444Split(ROOT+"lena_256x256_yuv444p.yuv",256,256,1);

运行上面代码后会在同目录下生成y、u、v三个文件

lena_256x256_yuv444p_u.y: Y数据, 分辨率为256x256lena_256x256_yuv444p_u.y: U数据, 分辨率为256x256lena_256x256_yuv444p_v.y: V数据, 分辨率为256x256

原始图片

lena_256x256_yuv444p.yuv

生成的y、u、v分量图片如下:

lena_256x256_yuv444p_y.y

lena_256x256_yuv444p_u.y

lena_256x256_yuv444p_v.y


3) 将YUV420P像素数据去掉颜色,变成灰度图

/** * Convert YUV420P file to gray picture * @param jstr_url Location of Input YUV file. * @param w Width of Input YUV file. * @param h Height of Input YUV file. * @param num Number of frames to process. * 将YUV420P像素数据去掉颜色,变成灰度图 * 如果想把YUV格式像素数据变成灰度图像,只需要将U、V分量设置成128即可。 * 这是因为U、V是图像中的经过偏置处理的色度分量。色度分量在偏置处理前的取值范围是-128至127,这时候的无色对应的是0值。经过偏置后色度分量取值变成了0至255,因而此时的无色对应的就是128了 */extern "C" JNIEXPORT jint JNICALLJava_com_xohn_ffmpeg_AVUtils_yuv420Gray( JNIEnv *env,jobject /* this */, jstring jstr_url, jint w, jint h, jint num) { char input_url[100] = {0}; const char *str = env->GetStringUTFChars(jstr_url, NULL); sprintf(input_url,"%s",str); env->ReleaseStringUTFChars(jstr_url,str); FILE *fp = fopen(input_url,"rb+"); if(fp == NULL) return -1; strcpy(input_url+strlen(input_url)-4,"_gray.yuv"); FILE *fp1 = fopen(input_url,"wb+"); unsigned char *pic=(unsigned char *)malloc(w*h*3/2); for(int i = 0;i < num;i++) { fread(pic, 1, w * h * 3 / 2, fp); //Gray memset(pic + w * h, 128, w * h / 2); fwrite(pic, 1, w * h * 3 / 2, fp1); } free(pic); fclose(fp); fclose(fp1); return 0;}

函数调用

AVUtils.getInstance().yuv420Gray(ROOT+"lena_256x256_yuv420p.yuv",256,256,1);

原始图片

lena_256x256_yuv420p.yuv

生成的灰度图如下:

lena_256x256_yuv420p_gray.yuv


4) 将YUV420P像素数据的亮度减半

/** * Halve Y value of YUV420P file * @param jstr_url Location of Input YUV file. * @param w Width of Input YUV file. * @param h Height of Input YUV file. * @param num Number of frames to process. * 将YUV420P像素数据的亮度减半 * 如果打算将图像的亮度减半,只要将图像的每个像素的Y值取出来分别进行除以2就可以了 */extern "C" JNIEXPORT jint JNICALLJava_com_xohn_ffmpeg_AVUtils_yuv420Halfy( JNIEnv *env,jobject /* this */, jstring jstr_url, jint w, jint h, jint num) { char input_url[100] = {0}; const char *str = env->GetStringUTFChars(jstr_url, NULL); sprintf(input_url,"%s",str); env->ReleaseStringUTFChars(jstr_url,str); FILE *fp = fopen(input_url,"rb+"); if(fp == NULL) return -1; strcpy(input_url+strlen(input_url)-4,"_halfy.yuv"); FILE *fp1 = fopen(input_url,"wb+"); unsigned char *pic=(unsigned char *)malloc(w*h*3/2); for(int i = 0;i < num;i++) { fread(pic, 1, w * h * 3 / 2, fp); //Half for (int j = 0; j < w * h; j++) { unsigned char temp = pic[j] / 2; pic[j] = temp; } fwrite(pic, 1, w * h * 3 / 2, fp1); } free(pic); fclose(fp); fclose(fp1); return 0;}

函数调用

AVUtils.getInstance().yuv420Halfy(ROOT+"lena_256x256_yuv420p.yuv",256,256,1);

原始图片

lena_256x256_yuv420p.yuv

生成的亮度减半图如下:

lena_256x256_yuv420p_halfy.yuv


5) 将YUV420P像素数据的周围加上边框

/** * Add border for YUV420P file * @param jstr_url Location of Input YUV file. * @param w Width of Input YUV file. * @param h Height of Input YUV file. * @param border Width of Border. * @param num Number of frames to process. * 将YUV420P像素数据的周围加上边框 * 图像的边框的宽度为border,本程序将距离图像边缘border范围内的像素的亮度分量Y的取值设置成了亮度最大值255 */extern "C" JNIEXPORT jint JNICALLJava_com_xohn_ffmpeg_AVUtils_yuv420Border( JNIEnv *env,jobject /* this */, jstring jstr_url, jint w, jint h, jint border, jint num) { char input_url[100] = {0}; const char *str = env->GetStringUTFChars(jstr_url, NULL); sprintf(input_url,"%s",str); env->ReleaseStringUTFChars(jstr_url,str); FILE *fp = fopen(input_url,"rb+"); if(fp == NULL) return -1; strcpy(input_url+strlen(input_url)-4,"_border.yuv"); FILE *fp1 = fopen(input_url,"wb+"); unsigned char *pic=(unsigned char *)malloc(w*h*3/2); for(int i = 0;i < num;i++){ fread(pic,1,w*h*3/2,fp); //Y for(int j = 0;j < h;j++){ for(int k = 0;k < w;k++){ if(k < border || k >= (w-border) || j < border || j >= (h-border)){ pic[j*w+k]=255; //pic[j*w+k]=0; } } } fwrite(pic,1,w*h*3/2,fp1); } free(pic); fclose(fp); fclose(fp1); return 0;}

函数调用

AVUtils.getInstance().yuv420Border(ROOT+"lena_256x256_yuv420p.yuv",256,256,20,1);

原始图片

lena_256x256_yuv420p.yuv

生成的周围加上边框图如下:

lena_256x256_yuv420p_border.yuv


6) 生成YUV420P格式的灰阶测试图

/** * Generate YUV420P gray scale bar. * @param width Width of Output YUV file. * @param height Height of Output YUV file. * @param ymin Max value of Y * @param ymax Min value of Y * @param barnum Number of bars * @param jstr_url_out Location of Output YUV file. * 生成YUV420P格式的灰阶测试图 * 通过灰阶测试图的亮度最小值ymin,亮度最大值ymax,灰阶数量barnum确定每一个灰度条中像素的亮度分量Y的取值。 * 还要根据图像的宽度width和图像的高度height以及灰阶数量barnum确定每一个灰度条的宽度 */extern "C" JNIEXPORT jint JNICALLJava_com_xohn_ffmpeg_AVUtils_yuv420GrayBar( JNIEnv *env,jobject /* this */, jstring jstr_url_out, jint width, jint height, jint ymin,jint ymax,jint barnum) { int barwidth; float lum_inc; unsigned char lum_temp; int uv_width,uv_height; FILE *fp=NULL; unsigned char *data_y = NULL; unsigned char *data_u = NULL; unsigned char *data_v = NULL; int t = 0,i = 0,j = 0; barwidth = width/barnum; lum_inc = ((float)(ymax-ymin))/((float)(barnum-1)); uv_width = width/2; uv_height = height/2; data_y = (unsigned char *)malloc(width*height); data_u = (unsigned char *)malloc(uv_width*uv_height); data_v = (unsigned char *)malloc(uv_width*uv_height); char input_url[100] = {0}; const char *str = env->GetStringUTFChars(jstr_url_out, NULL); sprintf(input_url,"%s",str); env->ReleaseStringUTFChars(jstr_url_out,str); if((fp=fopen(input_url,"wb+")) == NULL){ LOGE("Error: Cannot create file!"); return -1; } //Output Info LOGI("Y, U, V value from picture's left to right:\n"); for(t = 0;t < barnum;t++){ lum_temp = ymin+(char)(t*lum_inc); LOGI("%3d, 128, 128\n",lum_temp); } //Gen Data for(j = 0;j < height;j++){ for(i = 0;i < width;i++){ t = i/barwidth; lum_temp = ymin+(char)(t*lum_inc); data_y[j*width+i] = lum_temp; } } for(j = 0;j < uv_height;j++){ for(i = 0;i < uv_width;i++){ data_u[j*uv_width+i] = 128; } } for(j = 0;j < uv_height;j++){ for(i = 0;i < uv_width;i++){ data_v[j*uv_width+i] = 128; } } fwrite(data_y,width*height,1,fp); fwrite(data_u,uv_width*uv_height,1,fp); fwrite(data_v,uv_width*uv_height,1,fp); fclose(fp); free(data_y); free(data_u); free(data_v); return 0;}

函数调用

AVUtils.getInstance().yuv420GrayBar(ROOT+"graybar_640x360_yuv420p.yuv",640, 360,0,255,10);

生成的灰阶测试图如下:

graybar_640x360_yuv420p.yuv


7) 计算两个YUV420P像素数据的PSNR

/** * Calculate PSNR between 2 YUV420P file * @param jstr_url1 Location of first Input YUV file. * @param jstr_url2 Location of another Input YUV file. * @param w Width of Input YUV file. * @param h Height of Input YUV file. * @param num Number of frames to process. * 计算两个YUV420P像素数据的PSNR * PSNR是最基本的视频质量评价方法 * 程序计算后得到的PSNR取值为26.693。PSNR取值通常情况下都在20-50的范围内,取值越高,代表两张图像越接近,反映出受损图像质量越好 */extern "C" JNIEXPORT jint JNICALLJava_com_xohn_ffmpeg_AVUtils_yuv420Psnr( JNIEnv *env,jobject /* this */, jstring jstr_url1,jstring jstr_url2, jint w, jint h,jint num) { const char *str1 = env->GetStringUTFChars(jstr_url1, NULL); FILE *fp1 = fopen(str1,"rb+"); env->ReleaseStringUTFChars(jstr_url1,str1); if(fp1 == NULL) return -1; const char *str2 = env->GetStringUTFChars(jstr_url2, NULL); FILE *fp2 = fopen(str2,"rb+"); env->ReleaseStringUTFChars(jstr_url2,str2); if(fp2 == NULL) return -1; unsigned char *pic1 = (unsigned char *)malloc(w*h); unsigned char *pic2 = (unsigned char *)malloc(w*h); for(int i = 0;i < num;i++){ fread(pic1,1,w*h,fp1); fread(pic2,1,w*h,fp2); double mse_sum = 0,mse = 0,psnr = 0; for(int j = 0;j < w*h;j++){ mse_sum += pow((double)(pic1[j]-pic2[j]),2); } mse = mse_sum/(w*h); psnr = 10*log10(255.0*255.0/mse); LOGI("%5.3f\n",psnr); fseek(fp1,w*h/2,SEEK_CUR); fseek(fp2,w*h/2,SEEK_CUR); } free(pic1); free(pic2); fclose(fp1); fclose(fp2); return 0;}

函数调用

AVUtils.getInstance().yuv420Psnr(ROOT+"lena_256x256_yuv420p.yuv",ROOT+"lena_distort_256x256_yuv420p.yuv",256,256,1);

对于8bit量化的像素数据来说,PSNR的计算公式

PSNR的计算公式

原始图和受损图如下:

程序计算后得到的PSNR取值为26.693。PSNR取值通常情况下都在20-50的范围内,取值越高,代表两张图像越接近,反映出受损图像质量越好。


8) 分离RGB24像素数据中的R、G、B分量

/** * Split R, G, B planes in RGB24 file. * @param jstr_url Location of Input RGB file. * @param w Width of Input RGB file. * @param h Height of Input RGB file. * @param num Number of frames to process. * 分离RGB24像素数据中的R、G、B分量 * RGB24格式的每个像素的三个分量是连续存储的。一帧宽高分别为w、h的RGB24图像一共占用w*h*3 Byte的存储空间。 * RGB24格式规定首先存储第一个像素的R、G、B,然后存储第二个像素的R、G、B…以此类推。 * 类似于YUV420P的存储方式称为Planar方式,而类似于RGB24的存储方式称为Packed方式 */extern "C" JNIEXPORT jint JNICALLJava_com_xohn_ffmpeg_AVUtils_rgb24Split( JNIEnv *env,jobject /* this */, jstring jstr_url, jint w, jint h,jint num) { char input_url[100] = {0}; const char *str = env->GetStringUTFChars(jstr_url, NULL); sprintf(input_url,"%s",str); env->ReleaseStringUTFChars(jstr_url,str); FILE *fp = fopen(input_url,"rb+"); if(fp == NULL) return -1; strcpy(input_url+strlen(input_url)-4,"_r.y"); FILE *fp1 = fopen(input_url,"wb+"); strcpy(input_url+strlen(input_url)-4,"_g.y"); FILE *fp2 = fopen(input_url,"wb+"); strcpy(input_url+strlen(input_url)-4,"_b.y"); FILE *fp3 = fopen(input_url,"wb+"); unsigned char *pic=(unsigned char *)malloc(w*h*3); for(int i = 0;i < num;i++){ fread(pic,1,w*h*3,fp); for(int j = 0;j < w*h*3;j = j+3){ //R fwrite(pic+j,1,1,fp1); //G fwrite(pic+j+1,1,1,fp2); //B fwrite(pic+j+2,1,1,fp3); } } free(pic); fclose(fp); fclose(fp1); fclose(fp2); fclose(fp3); return 0;}

函数调用

AVUtils.getInstance().rgb24Split(ROOT+"cie1931_500x500.rgb",500,500,1);

运行上面代码后会在同目录下生成r、g、b三个文件

cie1931_500x500_r.y: R数据,分辨率为500x500cie1931_500x500_g.y:G数据,分辨率为500x500cie1931_500x500_b.y:B数据,分辨率为500x500

输入的原图是一张标准的CIE 1931色度图。该色度图右下为红色,上方为绿色,左下为蓝色

cie1931_500x500.rgb

生成的r、g、b分量图片如下:

cie1931_500x500_r.y

cie1931_500x500_g.y

cie1931_500x500_b.y


9) 将RGB24格式像素数据封装为BMP图像

/** * Convert RGB24 file to BMP file * @param jstr_rgb24path Location of input RGB file. * @param width Width of input RGB file. * @param height Height of input RGB file. * @param jstr_bmppath Location of Output BMP file. * 将RGB24格式像素数据封装为BMP图像 * BMP采用的是小端(Little Endian)存储方式。这种存储方式中“RGB24”格式的像素的分量存储的先后顺序为B、G、R。由于RGB24格式存储的顺序是R、G、B,所以需要将“R”和“B”顺序作一个调换再进行存储 * 该程序完成了主要完成了两个工作: * 1)将RGB数据素的“B”和“前面加上文件头。 * 2)将RGB数据中每个像R”的位置互换。 * BMP文件是由BITMAPFILEHEADER、BITMAPINFOHEADER、RGB像素数据共3个部分构成,它的结构如下所示。 * BITMAPFILEHEADER * BITMAPINFOHEADER * RGB像素数据 */extern "C" JNIEXPORT jint JNICALLJava_com_xohn_ffmpeg_AVUtils_rgb24ToBmp( JNIEnv *env,jobject /* this */, jstring jstr_rgb24path, jint width, jint height,jstring jstr_bmppath) { typedef struct { long imageSize; long blank; long startPosition; }BmpHead; typedef struct { long Length; long width; long height; unsigned short colorPlane; unsigned short bitColor; long zipFormat; long realSize; long xPels; long yPels; long colorUse; long colorImportant; }InfoHead; int i = 0,j = 0; BmpHead m_BMPHeader={0}; InfoHead m_BMPInfoHeader={0}; char bfType[2] = {'B','M'}; int header_size = sizeof(bfType)+sizeof(BmpHead)+sizeof(InfoHead); unsigned char *rgb24_buffer = NULL; FILE *fp_rgb24 = NULL,*fp_bmp = NULL; const char *rgb24path = env->GetStringUTFChars(jstr_rgb24path, NULL); if((fp_rgb24=fopen(rgb24path,"rb")) == NULL){ LOGE("Error: Cannot open input RGB24 file.\n"); env->ReleaseStringUTFChars(jstr_rgb24path,rgb24path); return -1; } env->ReleaseStringUTFChars(jstr_rgb24path,rgb24path); const char *bmppath = env->GetStringUTFChars(jstr_bmppath, NULL); if((fp_bmp=fopen(bmppath,"wb")) == NULL){ LOGE("Error: Cannot open output BMP file.\n"); env->ReleaseStringUTFChars(jstr_bmppath,bmppath); return -1; } rgb24_buffer = (unsigned char *)malloc(width*height*3); fread(rgb24_buffer,1,width*height*3,fp_rgb24); m_BMPHeader.imageSize = 3*width*height+header_size; m_BMPHeader.startPosition = header_size; m_BMPInfoHeader.Length = sizeof(InfoHead); m_BMPInfoHeader.width = width; //BMP storage pixel data in opposite direction of Y-axis (from bottom to top). m_BMPInfoHeader.height = -height; m_BMPInfoHeader.colorPlane = 1; m_BMPInfoHeader.bitColor = 24; m_BMPInfoHeader.realSize = 3*width*height; fwrite(bfType,1,sizeof(bfType),fp_bmp); fwrite(&m_BMPHeader,1,sizeof(m_BMPHeader),fp_bmp); fwrite(&m_BMPInfoHeader,1,sizeof(m_BMPInfoHeader),fp_bmp); //BMP save R1|G1|B1,R2|G2|B2 as B1|G1|R1,B2|G2|R2 //It saves pixel data in Little Endian //So we change 'R' and 'B' for(j = 0;j < height;j++){ for(i = 0;i < width;i++){ char temp = rgb24_buffer[(j*width+i)*3+2]; rgb24_buffer[(j*width+i)*3+2] = rgb24_buffer[(j*width+i)*3+0]; rgb24_buffer[(j*width+i)*3+0] = temp; } } fwrite(rgb24_buffer,3*width*height,1,fp_bmp); fclose(fp_rgb24); fclose(fp_bmp); free(rgb24_buffer); LOGI("Finish generate %s!\n",bmppath); env->ReleaseStringUTFChars(jstr_bmppath,bmppath); return 0;}

函数调用

AVUtils.getInstance().rgb24ToBmp(ROOT+"lena_256x256_rgb24.rgb",256,256,ROOT+"lena_256x256.bmp");

原始图片

lena_256x256_rgb24.rgb

生成的bmp可以用自带看图软件打开:

lena_256x256.bmp


10) 将RGB24格式像素数据转换为YUV420P格式像素数据

unsigned char clip_value(unsigned char x,unsigned char min_val,unsigned char max_val){ if(x > max_val){ return max_val; }else if(x < min_val){ return min_val; }else{ return x; }}//RGB to YUV420bool RGB24_TO_YUV420(unsigned char *RgbBuf,int w,int h,unsigned char *yuvBuf){ unsigned char*ptrY, *ptrU, *ptrV, *ptrRGB; LOGI("pic_rgb24 [ "); ptrRGB = RgbBuf; for(int ll = 0;ll < w*h*3;ll++){ LOGI("%c ",ptrRGB++); if(ll%50 == 0){LOGI("\n");} } LOGI("]\n"); memset(yuvBuf,0,w*h*3/2); ptrY = yuvBuf; ptrU = yuvBuf + w*h; ptrV = ptrU + (w*h*1/4); unsigned char y, u, v, r, g, b; for (int j = 0; j < h;j++){ ptrRGB = RgbBuf + w*j*3 ; //ptrRGB = RgbBuf + w * (h - 1 - j) * 3 ; for (int i = 0;i < w;i++){ r = *(ptrRGB++); g = *(ptrRGB++); b = *(ptrRGB++); /*y = (unsigned char)( ( 66 * r + 129 * g + 25 * b + 128) >> 8) + 16 ; u = (unsigned char)( ( -38 * r - 74 * g + 112 * b + 128) >> 8) + 128 ; v = (unsigned char)( ( 112 * r - 94 * g - 18 * b + 128) >> 8) + 128 ;*/ y = (unsigned char)( ( 66 * r + 129 * g + 25 * b) >> 8) + 16 ; u = (unsigned char)( ( -38 * r - 74 * g + 112 * b) >> 8) + 128 ; v = (unsigned char)( ( 112 * r - 94 * g - 18 * b) >> 8) + 128 ; *(ptrY++) = clip_value(y,0,255); if (j%2==0 && i%2 ==0){ *(ptrU++) = clip_value(u,0,255); } else{ if (i%2==0){ *(ptrV++) = clip_value(v,0,255); } } } } return true;}/** * Convert RGB24 file to YUV420P file * @param url_in Location of Input RGB file. * @param w Width of Input RGB file. * @param h Height of Input RGB file. * @param num Number of frames to process. * @param url_out Location of Output YUV file. * RGB24格式像素数据转换为YUV420P格式像素数据 * RGB到YUV的转换公式: * Y = 0.257*R' + 0.504*G' + 0.098*B' + 16 * U = -0.148*R' - 0.291*G' + 0.439*B' + 128 * V = 0.439*R' - 0.368*G' - 0.071*B' + 128 * 转换的过程中有以下几点需要注意: * 1) RGB24存储方式是Packed,YUV420P存储方式是Planar。 * 2) U,V在水平和垂直方向的取样数是Y的一半 */extern "C" JNIEXPORT jint JNICALLJava_com_xohn_ffmpeg_AVUtils_rgb24ToYuv420( JNIEnv *env,jobject /* this */, jstring jstr_urlin, jint w, jint h, jint num,jstring jstr_urlout) { const char *url_in = env->GetStringUTFChars(jstr_urlin, NULL); LOGI("url_in : %s\n",url_in); FILE *fp = fopen(url_in,"rb+"); env->ReleaseStringUTFChars(jstr_urlin,url_in); if(fp == NULL){ return -1; } const char *urlout = env->GetStringUTFChars(jstr_urlout, NULL); LOGI("urlout : %s\n",urlout); FILE *fp1 = fopen(urlout,"wb+"); env->ReleaseStringUTFChars(jstr_urlout,urlout); if(fp1 == NULL){ return -1; } unsigned char *pic_rgb24 = (unsigned char *)malloc(w*h*3); unsigned char *pic_yuv420 = (unsigned char *)malloc(w*h*3/2); for(int i = 0;i < num;i++){ fread(pic_rgb24,1,w*h*3,fp); RGB24_TO_YUV420(pic_rgb24,w,h,pic_yuv420); fwrite(pic_yuv420,1,w*h*3/2,fp1); } free(pic_rgb24); free(pic_yuv420); fclose(fp); fclose(fp1); return 0;}

函数调用

AVUtils.getInstance().rgb24ToYuv420(ROOT+"lena_256x256_rgb24.rgb",256,256,1,ROOT+"lena_256x256_rgb24ToYuv420.yuv");

原始图片

lena_256x256_rgb24.rgb

转换后的YUV420P格式的图片:

lena_256x256_rgb24ToYuv420.yuv


11) 生成RGB24格式的彩条测试图

/** * Generate RGB24 colorbar. * @param width Width of Output RGB file. * @param height Height of Output RGB file. * @param url_out Location of Output RGB file. * 生成RGB24格式的彩条测试图 */extern "C" JNIEXPORT jint JNICALLJava_com_xohn_ffmpeg_AVUtils_rgb24ColorBar( JNIEnv *env,jobject /* this */, jint width, jint height,jstring jstr_urlout) { unsigned char *data = NULL; int barwidth; FILE *fp = NULL; int i = 0,j = 0; data = (unsigned char *)malloc(width*height*3); barwidth = width/8; const char *url_out = env->GetStringUTFChars(jstr_urlout, NULL); fp = fopen(url_out,"wb+"); env->ReleaseStringUTFChars(jstr_urlout,url_out); if(fp == NULL){ return -1; } for(j = 0;j < height;j++){ for(i = 0;i < width;i++){ int barnum = i/barwidth; switch(barnum){ case 0:{ data[(j*width+i)*3+0] = 255; data[(j*width+i)*3+1] = 255; data[(j*width+i)*3+2] = 255; break; } case 1:{ data[(j*width+i)*3+0] = 255; data[(j*width+i)*3+1] = 255; data[(j*width+i)*3+2] = 0; break; } case 2:{ data[(j*width+i)*3+0] = 0; data[(j*width+i)*3+1] = 255; data[(j*width+i)*3+2] = 255; break; } case 3:{ data[(j*width+i)*3+0] = 0; data[(j*width+i)*3+1] = 255; data[(j*width+i)*3+2] = 0; break; } case 4:{ data[(j*width+i)*3+0] = 255; data[(j*width+i)*3+1] = 0; data[(j*width+i)*3+2] = 255; break; } case 5:{ data[(j*width+i)*3+0] = 255; data[(j*width+i)*3+1] = 0; data[(j*width+i)*3+2] = 0; break; } case 6:{ data[(j*width+i)*3+0] = 0; data[(j*width+i)*3+1] = 0; data[(j*width+i)*3+2] = 255; break; } case 7:{ data[(j*width+i)*3+0] = 0; data[(j*width+i)*3+1] = 0; data[(j*width+i)*3+2] = 0; break; } } } } fwrite(data,width*height*3,1,fp); fclose(fp); free(data); return 0;}

函数调用

AVUtils.getInstance().rgb24ColorBar(640, 360,ROOT+"rgb24colorbar_640x360.rgb");

生成“白黄青绿品红蓝黑”8种颜色的彩条,如下图:

电脑

rgb24colorbar_640x360.rgb


参考文章:

雷神文章 https://blog.csdn.net/leixiaohua1020/article/details/50534150

RGB到YUV的转换公式 https://blog.csdn.net/xiaoyafang123/article/details/82153279


项目git地址:

https://gitee.com/xohn/FFmpeg.git 中的FFMpeg1


电脑
责任编辑:电脑知识学习网