21xrx.com
2024-12-22 16:40:02 Sunday
登录
文章检索 我的文章 写文章
C++实现YUV转PNG
2023-07-04 20:49:44 深夜i     --     --
C++ YUV PNG 转换 实现

YUV是一种常见的视频色彩空间,其包含亮度(Y)和色度(U和V)三个分量,常见于数字视频、视频编解码等应用中。PNG是一种通过无损压缩方式来保存图像的文件格式。本文介绍了使用C++来实现YUV到PNG的转换方法。

首先需要了解YUV的编码方式。对于YUV420格式而言,每个像素点都包含一个亮度值和一个色度值,但是色度值是按照一定的规则进行采样的。通常是把4x4的像素块按照某个规则进行采样,得到8个U值和8个V值,这样就实现了对原图进行了压缩。

实现YUV到PNG的转换需要借助第三方库libpng和libjpeg-turbo。libpng用于生成PNG文件,libjpeg-turbo用于读取YUV420文件。以下是具体的实现步骤:

1. 首先需要将YUV420文件读入内存中,这里可以借助libjpeg-turbo的库函数进行读取。

2. 读取完成后,可以根据图像的分辨率和采样方式,计算出每个YUV像素对应的RGB值。

3. 然后将RGB值写入到libpng生成的PNG文件中即可。

以下是具体的代码实例:


// include libpng and libjpeg-turbo headers

#include <png.h>

#include <jpeglib.h>

// define RGB pixel structure

typedef struct

  unsigned char r RGBPixel;

int yuvToRGB(int y, int u, int v) // convert a single YUV pixel to RGB

{

  // first calculate R, G, and B components

  int r = y + (1.370705 * (v-128));

  int g = y - (0.698001 * (v-128)) - (0.337633 * (u-128));

  int b = y + (1.732446 * (u-128));

  // clamp values to 0-255

  r = r<0? 0: r>255? 255: r;

  g = g<0? 0: g>255? 255: g;

  b = b<0? 0: b>255? 255: b;

  // combine R, G, and B into a single integer

  return (r << 16) | (g << 8) | G

RGBPixel* yuv420ToRGB(unsigned char* data, int width, int height)

{

  int yIndex, uIndex, vIndex, rIndex;

  int y, u, v, r, g, b;

  RGBPixel* output = new RGBPixel[width*height];

  // loop through pixels in 4x4 blocks

  for(int i=0; i<height; i+=2) {

    for(int j=0; j<width; j+=2) {

      // calculate indices for current block

      yIndex = i*width + j; uIndex = i/2*width/2 + j/2; vIndex = uIndex;

      rIndex = yIndex;

      // calculate RGB values for each pixel in the block

      y = data[yIndex];

      u = data[uIndex+width*height];

      v = data[vIndex+width*height*5/4];

      r = yuvToRGB(y, u, v);

      output[rIndex].r = (r >> 16) & 0xFF;

      output[rIndex].g = (r >> 8) & 0xFF;

      output[rIndex].b = r & 0xFF;

      y = data[yIndex+1];

      rIndex++;

      r = yuvToRGB(y, u, v);

      output[rIndex].r = (r >> 16) & 0xFF;

      output[rIndex].g = (r >> 8) & 0xFF;

      output[rIndex].b = r & 0xFF;

      y = data[yIndex+width];

      rIndex += width-1;

      r = yuvToRGB(y, u, v);

      output[rIndex].r = (r >> 16) & 0xFF;

      output[rIndex].g = (r >> 8) & 0xFF;

      output[rIndex].b = r & 0xFF;

      y = data[yIndex+width+1];

      rIndex++;

      r = yuvToRGB(y, u, v);

      output[rIndex].r = (r >> 16) & 0xFF;

      output[rIndex].g = (r >> 8) & 0xFF;

      output[rIndex].b = r & 0xFF;

    }

  }

  return output;

}

void writePNG(RGBPixel* data, int width, int height, const char* filename)

{

  FILE* file = fopen(filename, "wb");  

  png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

  png_infop info_ptr = png_create_info_struct(png_ptr);

  png_init_io(png_ptr, file);

  png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

  png_write_info(png_ptr, info_ptr);

  png_bytep row_pointer;

  row_pointer = (png_bytep) malloc(width * 3);

  for (int y=0; y<height; y++) {

    int row_start = y*width;

    for(int x=0; x<width; x++) {

      row_pointer[x*3] = data[row_start+x].r;

      row_pointer[x*3+1] = data[row_start+x].g;

      row_pointer[x*3+2] = data[row_start+x].b;

    }

    png_write_row(png_ptr, row_pointer);

  }

  png_write_end(png_ptr, NULL);

  fclose(file);

  png_destroy_write_struct(&png_ptr, &info_ptr);

}

void yuvToPNG(const char* yuv_filename, const char* png_filename, int width, int height)

{

  // read YUV data from file

  FILE* input_file = fopen(yuv_filename, "rb");

  unsigned char* data = new unsigned char[width*height*3/2];

  fread(data, 1, width*height*3/2, input_file);

  fclose(input_file);

  // convert YUV to RGB

  RGBPixel* rgb_data = yuv420ToRGB(data, width, height);

  delete[] data;

  // write RGB data to PNG file

  writePNG(rgb_data, width, height, png_filename);

  delete[] rgb_data;

}

使用以上代码,就可以将一个YUV420文件转换成为一个PNG文件。调用函数`yuvToPNG`,传入YUV文件名、要生成的PNG文件名,以及图像的分辨率,即可完成转换。

  
  

评论区

{{item['qq_nickname']}}
()
回复
回复