实验目的
- 熟悉BMP文件结构,进一步学习多媒体文件结构设计的思想。
- 编程读取BMP文件,要求程序可以处理1、2、4、8、24、32bit色深。
- 实践RGB与YUV色彩空间的转换,将BMP文件转换至YUV色彩空间。
实验原理
RGB与YUV空间的相互转换:
由电视原理可知,亮度和色差信号的构成如下:
Y=0.2990R+0.5870G+0.1140B
R-Y=0.7010R-0.5870G-0.1140B
B-Y=-0.2990R-0.5870G+0.8860B
为了使色差信号的动态范围控制在0.5之间,需要进行归一化,对色差信号引入压缩
系数。归一化后的色差信号为:
U=-0.1684R-0.3316G+0.5B
V=0.5R-0.4187G-0.0813B
码电平分配:
在对亮度分量信号进行8比特均匀量化时,共分为256个等间隔的量化级。为了防止信号变动造成过载,在256级上端留20级,下端留16级作为信号超越动态范围的保护带。色差信号经过归一化处理后,动态范围为-0.5-0.5,让色差零电平对应码电平128,色差信号总共占225个量化级。在256级上端留15级,下端留16级作为信号超越动态范围的保护带。
色度格式:
在数字电视中,对亮度信号和两个色差信号的分量信号有以 下几种取样格式:
• 4:4:4:表示RGB取样,RGB信号的取样点数量一样。
• 4:2:2:色差信号U和V在水平方向取样点数为Y的二分之一,而垂直方向取样点数与Y相同。
• 4:2:0:色差信号U和V在水平和垂直方向的取样点数均为Y的二分之一。
• 4:1:1:色差信号Cr和Cb在水平方向取样点数为Y的四分之一,而 垂直方向取样点数与Y相同。
本次实验中采用4:2:0色度取样格式。
实验中,程序调用easyx绘图库实现对图像的显示。
BMP文件读取
BMP文件结构
其中:
- bmp文件头定义如下:
typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
名称 | 数据类型 | 说明 |
---|---|---|
bfType | WORD | 说明文件的类型 |
bfSize | DWORD | 说明文件的大小,用字节为单位 |
bfReserved1 | WORD | 保留,设置为0 |
bfReserved2 | WORD | 保留,设置为0 |
bfOffBits | DWORD | 说明从BITMAPFILEHEADER结构 开始到实际的图像数据之间的字节偏移量 |
- 位图信息头定义如下:
typedef struct tagBITMAPINFOHEADER {
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;
名称 | 数据类型 | 说明 |
---|---|---|
biSize | DWORD | 说明结构体所需字节数 |
biWidth | LONG | 以像素为单位说明图像的宽度 |
biHeight | LONG | 以像素为单位说明图像的高度 |
biPlanes | WORD | 说明位面数,必须为1 |
biBitCount | WORD | 说明位数/像素,1、2、4、8、24 |
biCompression | DWORD | 说明图像是否压缩及压缩类型 BI_RGB,BI_RLE8,BI_RLE4,BI_BITFIELDS |
biSizeImage | DWORD | 以字节为单位说明图像大小,必须是4的整数倍,当用BI_RGB格式时,可以设置为0 |
biXPelsPerMeter | LONG | 目标设备的水平分辨率,像素/米 |
biYPelsPerMeter | LONG | 目标设备的垂直分辨率,像素/米 |
biClrUsed | DWORD | 说明图像实际用到的颜色数,如果为0 则颜色数为2的biBitCount次方 |
biClrImportant | DWORD | 说明对图像显示有重要影响的颜色 索引的数目,如果是0,表示都重要。 |
- 调色板信息如下 :
typedef struct tagRGBQUAD {
BYTE rgbBlue; /\*指定蓝色分量\*/
BYTE rgbGreen; /\*指定绿色分量\*/
BYTE rgbRed; /\*指定红色分量\*/
BYTE rgbReserved; /\*保留,指定为0\*/
} RGBQUAD;
调色板实际上是一个数组,它所包含的元素与位图 所具有的颜色数相同,决定于biClrUsed和biBitCount字 段。数组中每个元素的类型是一个RGBQUAD结构。真彩色(24bit和32bit)无调色板部分。
- 关于位图数据的说明:
Windows默认的扫描的最小单位是4字节,因此在BMP图像中要求每行的数据的长度必须是4的倍数,如果不够需要进行比特填充(默认为0)。
当biHeight为正数时,扫描行是由底向上存储的,这就是说,阵列中的第一个字节 表示位图左下角的像素,而最后一个字节表示位图右上角的像素;否则为正向存储。
每行数据量计算公式如下:
row_size = ((DWORD)(width * depth + 31) >> 5) << 2;
实验素材
1bit、4bit、8bit及24bit BMP位图图像:
8bit大尺寸图片:
转换至YUV420采样的图像(24bit):
实验结果
BMP文件读取
1bit文件读取结果
4bit文件读取结果
8bit文件读取结果
24bit文件读取结果
8bit大尺寸图片读取结果
转换用24bit图片读取结果
转换至YUV色彩空间后结果
误差分析
造成色彩空间转换的误差来源有:
- 1、计算误差,由上文转换公式可知,直接计算后的结果是一个浮点型数据,但实际存储和表示图片像素都是使用的8bit unsigned char型数据,舍弃小数点后小数造成误差。
- 2、保护边带造成的误差,在实际计算中YUV数据都会进入保护边带内,但根据转换要求需要将进入边带的数值更改为边界值,因此造成了转换误差。
- 3、色度采样误差,由于采用4:2:0色度采样格式,每四个像素共用一个色度采样值,因此导致了一部分色度信息的丢失,转换回RGB色彩空间时导致误差。
关于rgb色彩空间与yuv色彩空间更详细的转换误差分析如下:
数据压缩学习实验(一)RGB与YUV色彩空间转换的C++实现及误差分析
一些笔记
- 1、biSizeImage当用BI_RGB格式时,可以设置为0,一开始编程时在程序中使用这个数据,读取某些为0的图片时报错。实际数据大小需要计算。
- 2、文件储存顺序问题,bitmap文件采用little-endian顺序储存,即多字节数据低字节存放在低位地址,高字节存放在高位地址。
- 3、读取文件中某比特尝试过多种方法,目前认为较为简单的是先将数据根据位数位移到低位,再与上相应的数据(如取后四位就与0x15,二进制00001111),这样再读取不同色深时较为灵活。
- 4、读取4个小图像时上下行颜色不对,但读取其它图片时无此问题,原因尚未查明。
程序代码
bmp_file.h
#include<windows.h>
#pragma once
#ifndef qqq
#define qqq
typedef unsigned char uint1;
typedef unsigned short uint2;
typedef unsigned int uint4;
typedef int int4;
class BMP {
#pragma(push)
#pragma(2)
private:
BITMAPFILEHEADER header;
BITMAPINFOHEADER info;
uint4 color_table_size;
RGBQUAD* color_table;
uint4 data_size;
uint1* data;
float Byte_per_pixel;
int depth;
int width;
int height;
int row_size;
bool create(uint4 w, uint4 h);
bool read(const char* path);
uint4 calculate_index(uint4 w, uint4 h);
#pragma(pop)
public:
explicit BMP(uint4 w, uint4 h);
explicit BMP(const char* path);
~BMP();
RGBTRIPLE* get_pixel(uint4 x, uint4 y);
int get_width();
int get_height();
uint2 get_depth();
uint1* get_data();
void show();
};
#endif // !qqq
bmp_file.cpp
#include "BMP_file.h"
#include <Windows.h>
#include <easyx.h>
#include <conio.h>
#include <iostream>
#include <fstream>
using namespace std;
bool BMP::create(uint4 w, uint4 h)
{
Byte_per_pixel = 3;
width = w;
height = h;
depth = 24;
row_size = ((width * depth + 31) >> 5) << 2;
data_size = height * row_size * 3;
header.bfType = 19778;
header.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
header.bfReserved1 = 0;
header.bfReserved2 = 0;
header.bfSize = header.bfOffBits + w * h;
info.biSize = sizeof(BITMAPINFOHEADER);
info.biWidth = width;
info.biHeight = height;
info.biPlanes = 1;
info.biBitCount = 24;
info.biCompression = 0;
info.biSizeImage = height * row_size;
info.biXPelsPerMeter = 128;
info.biYPelsPerMeter = 128;
info.biClrUsed = 0;
info.biClrImportant = 0;
color_table_size = 0;
color_table = nullptr;
data = new uint1[data_size];
if (data == nullptr) {
cerr << __func__ << "()\tCan not get memory!" << endl;
return false;
}
else {
return true;
}
}
bool BMP::read(const char* path)
{
ifstream file_in(path, ios::binary);
if (!file_in.is_open()) {
cerr << __func__ << "\t Can not open file: " << path << endl;
return false;
}
file_in.read(reinterpret_cast<char*>(&header), sizeof(BITMAPFILEHEADER));
if (header.bfType != 19778) {
cerr << __func__ << "\t Not a bitmap file!" << endl;
return false;
}
file_in.read(reinterpret_cast<char*>(&info), sizeof(BITMAPINFOHEADER));
width = info.biWidth;
height = info.biHeight;
depth = info.biBitCount;
switch (depth)
{
case(24): {
Byte_per_pixel = 3;
}break;
case(32): {
Byte_per_pixel = 4;
}break;
case(8): {
Byte_per_pixel = 1;
}break;
case(4): {
Byte_per_pixel = 0.5;
}break;
case(2): {
Byte_per_pixel = 0.25;
}break;
case(1): {
Byte_per_pixel = 0.125;
}break;
default: {
cerr << __func__ << "()\tInvalid depth!" << endl;
return false;
}
}
/*calculate the pixels per line that number must be int*4 */
row_size = ((uint4)(width * depth + 31) >> 5) << 2;
data_size = width * height * Byte_per_pixel; //can not use biBitCount, maybe = 0
if (header.bfOffBits < 54) {
cerr << __func__ << "()\tInvalid offbits!" << endl;
return false;
}
else if (header.bfOffBits == 54) {
color_table_size = 0;
color_table = nullptr;
}
else {
color_table_size = (header.bfOffBits - 54) / 4;
color_table = new RGBQUAD[color_table_size];
if (color_table == nullptr) {
cerr << __func__ << "()\tCan not get memory!" << endl;
return false;
}
file_in.read(reinterpret_cast<char*>(color_table), color_table_size * 4);
}
data = new uint1[data_size];
if (data == nullptr) {
cerr << __func__ << "()\tCan not get memory!" << endl;
return false;
}
file_in.read(reinterpret_cast<char*>(data), data_size);
file_in.close();
}
uint4 BMP::calculate_index(uint4 w, uint4 h)
{
/*return the number of bytes, include all cases of depth*/
uint4 index(0);
if (height > 0) {
index = (height - h - 1) * row_size + (uint4)(w * Byte_per_pixel);
}
else {
index = h * row_size + (uint4)(w * Byte_per_pixel);
}
return index;
}
BMP::BMP(uint4 w, uint4 h)
{
if (!create(w, h)) {
system("pause");
exit(1);
}
}
BMP::BMP(const char* path)
{
read(path);
}
BMP::~BMP()
{
closegraph();
if (color_table != nullptr) {
delete[] color_table;
}
if (data != nullptr) {
delete[] data;
}
}
RGBTRIPLE* BMP::get_pixel(uint4 x, uint4 y)
{
if (x >= width || y >= height) {
cerr << __func__ << "\tInvalid position!" << endl;
return nullptr;
}
uint4 index = calculate_index(x, y);
uint1 pixel;
switch (depth)
{
case(32):
case(24): {
uint1* dat;
dat = data + index;
return reinterpret_cast<RGBTRIPLE*>(data + index);
}break;
case(8): {
pixel = *(data + index);
return reinterpret_cast<RGBTRIPLE*>(color_table + pixel);
}break;
case(4): {
pixel = (*(data + index) >> 4 * (1 - (x % 2))) & 15; // move 4 bits to the end and extract them
return reinterpret_cast<RGBTRIPLE*>(color_table + pixel);
}break;
case(2): {
pixel = (*(data + index) >> 2 * (1 - (x % 4))) & 3; // move 2 bits to the end and extract them
return reinterpret_cast<RGBTRIPLE*>(color_table + pixel);
}break;
case(1): {
pixel = (*(data + index) >> (7 - (x % 8))) & 1; // move 1 bits to the end and extract them
return reinterpret_cast<RGBTRIPLE*>(color_table + pixel);
}break;
default: {
cerr << __func__ << "\tThe depth may is 16bits (555 or 565) which can't be read!" << endl;
return nullptr;
}
}
}
int BMP::get_width()
{
return width;
}
int BMP::get_height()
{
return height;
}
uint2 BMP::get_depth()
{
return depth;
}
uint1* BMP::get_data()
{
return this->data;
}
void BMP::show( )
{
initgraph(width, height, SHOWCONSOLE);
cout << "loading..." << endl;
RGBTRIPLE* bgr;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
bgr = this->get_pixel(j, i);
putpixel(j, i, RGB(bgr->rgbtRed, bgr->rgbtGreen, bgr->rgbtBlue));
}
}
cout << "complete!" << endl;
}
YUV_raw.h
#ifndef YUV_img
#define YUV_img
#pragma once
typedef unsigned char uint1;
typedef unsigned short uint2;
typedef unsigned int uint4;
typedef int int4;
#pragma pack(push)
#pragma pack(2)
typedef struct {
uint1* Y_data;
uint1* U_data;
uint1* V_data;
}YUV_per_frame, YUV;
enum chrominace_mode { C444, C422, C420, C411 };
class YUV_raw_image {
protected:
uint4 width;
uint4 height;
chrominace_mode mode;
uint4 C_width; //width of chrominace
uint4 C_height; //height of chrominace
uint4 frame_rate;
uint4 frame_num;
uint4 frame_size;
uint4 frame_Y_size;
uint4 frame_U_size;
uint4 frame_V_size;
YUV_per_frame* data;
#pragma pack(pop)
public:
explicit YUV_raw_image(uint2 w, uint2 h, uint2 f_rate, chrominace_mode m, uint4 num = 1);
explicit YUV_raw_image(uint2 w, uint2 h, uint2 f_rate, chrominace_mode m, const char* filePath);
virtual ~YUV_raw_image();
void init(uint2 w, uint2 h, uint2 f_rate, chrominace_mode m);
bool read_frame(const char* filePath);
bool save(const char* filePath);
uint2 get_width();
uint2 get_height();
chrominace_mode get_mode();
YUV get_pixel(uint2 w, uint2 h, uint4 f = 0);
YUV* get_data(uint4 f = 0);
};
#endif
YUV_raw.cpp
#include "YUV_raw.h"
#include <iostream>
#include <fstream>
using namespace std;
YUV_raw_image::YUV_raw_image(uint2 w, uint2 h, uint2 f_rate, chrominace_mode m, uint4 num)
{
init(w,h,f_rate,m);
frame_num = num;
data = new YUV_per_frame[frame_num];
for (int i = 0; i < frame_num; i++) {
uint1* ptr = new uint1[frame_size]{ 0 };//set values of pixel to 0
(data + i)->Y_data = ptr;
(data + i)->U_data = ptr + frame_Y_size;
(data + i)->V_data = ptr + frame_Y_size + frame_U_size;
}
}
YUV_raw_image::YUV_raw_image(uint2 w, uint2 h, uint2 f_rate, chrominace_mode m, const char* filePath)
{
init(w, h, f_rate, m);
try {
if (!read_frame(filePath)) {
throw ("Failed to create object\n");
}
}
catch (const char* msg) {
cerr << msg << endl;
system("pause");
exit(114514);
}
}
YUV_raw_image::~YUV_raw_image()
{
for (int i = 0; i < frame_num; i++) {
delete[](data + i)->Y_data;
}
delete[] data;
}
void YUV_raw_image::init(uint2 w, uint2 h, uint2 f_rate, chrominace_mode m)
{
width = w;
height = h;
mode = m;
frame_rate = f_rate;
switch (mode) {
case(C444): {
C_width = width;
C_height = height;
}break;
case(C422): {
C_width = (uint2)(width / 2);
C_height = height;
}break;
case(C420): {
C_width = (uint2)(width / 2);
C_height = (uint2)(height / 2);
}break;
case(C411): {
C_width = (uint2)(width / 4);
C_height = height;
}break;
}
frame_Y_size = width * height;
frame_U_size = C_width * C_height;
frame_V_size = C_width * C_height;
frame_size = frame_Y_size + frame_U_size + frame_V_size;
}
bool YUV_raw_image::read_frame(const char* filePath)
{
std::ifstream file_in(filePath, ios::binary); //ios::binary means open a binary file.
if (file_in.is_open()) {
/*obtain the address of feader, then convert it into buffer which point to type char*/
/*the order of data in struct BMPFileHeader should be same as it's in files*/
istream::pos_type start_pos = file_in.tellg();
file_in.seekg(0, ios_base::end);
istream::pos_type end_pos = file_in.tellg();
file_in.seekg(start_pos);
istream::pos_type file_size = end_pos - start_pos;
if ((file_size % frame_size) != 0) {
cout << "The file is not entire!" << endl;
return false;
}
else {
frame_num = file_size / frame_size;
}
data = new YUV_per_frame[frame_num];
for (int i = 0; i < frame_num; i++) {
uint1* ptr = new uint1[frame_size];
file_in.read((char*)ptr, frame_size);
(data + i)->Y_data = ptr; //set values of pixel to 0
(data + i)->U_data = ptr + frame_Y_size;
(data + i)->V_data = ptr + frame_Y_size + frame_U_size;
}
file_in.close();
cout << "Read finished!" << endl;
cout << "The number of frame is " << frame_num << endl;
return true;
}
else {
cout << __func__ << "Failed to open input file" << filePath << endl;
return false;
}
}
bool YUV_raw_image::save(const char* filePath)
{
YUV_per_frame* p = data;
std::ofstream file_out(filePath, ios::binary); //ios::binary means open a binary file.
if (file_out.is_open()) {
for (int i = 0; i < frame_num; i++, p++) {
file_out.write((char*)p->Y_data, frame_Y_size);
file_out.write((char*)p->U_data, frame_U_size);
file_out.write((char*)p->V_data, frame_V_size);
}
file_out.close();
return true;
}
else {
cout << __func__ << "Failed to open output file" << filePath << endl;
return false;
}
}
uint2 YUV_raw_image::get_width()
{
return width;
}
uint2 YUV_raw_image::get_height()
{
return height;
}
chrominace_mode YUV_raw_image::get_mode()
{
return mode;
}
YUV YUV_raw_image::get_pixel(uint2 w, uint2 h, uint4 f)
{
uint1* y(nullptr);
uint1* u(nullptr);
uint1* v(nullptr);
uint2 index_x(0), index_y(0);
switch (mode) {
case(C444): {
index_x = w;
index_y = h;
}break;
case(C422): {
index_x = (uint2)(w / 2);
index_y = h;
}break;
case(C420): {
index_x = (uint2)(w / 2);
index_y = (uint2)(h / 2);
}break;
case(C411): {
C_width = (uint2)(w / 4);
C_height = height;
}break;
}
if (f < frame_num && w < width && h < height) {
y = ((data + f)->Y_data) + (index_y * width + index_x);
u = ((data + f)->U_data) + (index_y * C_width + index_x);
v = ((data + f)->V_data) + (index_y * C_width + index_x);
}
return YUV{ y,u,v };
}
YUV* YUV_raw_image::get_data(uint4 f)
{
return data + f;
}
converter.h
#ifndef tr
#define tr
#include "BMP_file.h"
#include "YUV_raw.h"
void init_LookupTable();
uint1 clamp_Y(int t);
uint1 clamp_UV(int t);
uint1 clamp_RGB(int t);
bool bmp2yuv(BMP &img, YUV_raw_image& yuv, uint4 frame = 0);
#endif
converter.cpp
#include "converter.h"
#include <iostream>
#include <fstream>
using namespace std;
static uint1 RGBYUV02290[256];
static uint1 RGBYUV05870[256];
static uint1 RGBYUV01140[256];
static uint1 RGBYUV01684[256];
static uint1 RGBYUV03316[256];
static uint1 RGBYUV05000[256];
static uint1 RGBYUV04187[256];
static uint1 RGBYUV00813[256];
static int YUVRGB14020[256];
static int YUVRGB07141[256];
static int YUVRGB03144[256];
static int YUVRGB17720[256];
static bool init_flg = false;
void init_LookupTable()
{
if (init_flg == false) {
for (int i = 0; i < 256; i++) {
RGBYUV02290[i] = (float)i * 0.299;
RGBYUV05870[i] = (float)i * 0.587;
RGBYUV01140[i] = (float)i * 0.114;
RGBYUV01684[i] = (float)i * 0.1684;
RGBYUV03316[i] = (float)i * 0.3316;
RGBYUV05000[i] = (float)i * 0.5;
RGBYUV04187[i] = (float)i * 0.4187;
RGBYUV00813[i] = (float)i * 0.0813;
YUVRGB14020[i] = (float)(i - 128) * 1.402;
YUVRGB07141[i] = (float)(i - 128) * 0.71414;
YUVRGB03144[i] = (float)(i - 128) * 0.314414;
YUVRGB17720[i] = (float)(i - 128) * 1.772;
}
init_flg = true;
}
}
uint1 clamp_Y(int t)
{
if (t < 16) {
return 16;
}
else if (t > 235) {
return 235;
}
else {
return t;
}
}
uint1 clamp_UV(int t)
{
if (t < 16) {
return 16;
}
else if (t > 240) {
return 240;
}
else {
return t;
}
}
uint1 clamp_RGB(int t)
{
if (t < 0) {
return 0;
}
else if (t > 255) {
return 255;
}
else {
return t;
}
}
bool bmp2yuv(BMP& img, YUV_raw_image& yuv, uint4 frame)
{
if (init_flg == false) {
init_LookupTable();
}
int width = img.get_width();
int height = img.get_height();
chrominace_mode mode = yuv.get_mode();
if (yuv.get_width() != width || yuv.get_height() != height) {
cerr << __func__ << "\t The size of yuv file doesn\t match with bmp!" << endl;
return false;
}
int y, u, v;
RGBTRIPLE* bgr_data;
YUV* yuv_data = yuv.get_data();
/*loop to traverse all pixels*/
int loop_y(0), loop_uv(0);
for (int loop_i = 0; loop_i < height; loop_i++) {
for (int loop_j = 0; loop_j < width; loop_j++) {
bgr_data = img.get_pixel(loop_j, loop_i);
y = RGBYUV02290[bgr_data->rgbtRed] + RGBYUV05870[bgr_data->rgbtGreen]
+ RGBYUV01140[bgr_data->rgbtBlue];
u = RGBYUV05000[bgr_data->rgbtBlue] - RGBYUV01684[bgr_data->rgbtRed]
- RGBYUV03316[bgr_data->rgbtGreen] + 128;
v = RGBYUV05000[bgr_data->rgbtRed] - RGBYUV04187[bgr_data->rgbtGreen]
- RGBYUV00813[bgr_data->rgbtBlue] + 128;
y = clamp_Y(y);
u = clamp_UV(u);
v = clamp_UV(v);
switch (mode)
{
case(C444): {
*(yuv_data->Y_data + loop_y) = y;
*(yuv_data->U_data + loop_y) = u;
*(yuv_data->V_data + loop_y) = v;
}break;
case(C422): {
*(yuv_data->Y_data + loop_y) = y;
if (loop_y % 2 == 0) {
*(yuv_data->U_data + loop_uv) = u;
*(yuv_data->V_data + loop_uv) = v;
loop_uv++;
}
}break;
case(C420): {
*(yuv_data->Y_data + loop_y) = y;
if ((loop_y % 2 == 0) && (((loop_y - (loop_y % width)) / width) % 2 == 0)) {
/*this condition means the position of chrominace sample*/
*(yuv_data->U_data + loop_uv) = u;
*(yuv_data->V_data + loop_uv) = v;
loop_uv++;
}
}break;
case(C411): {
*(yuv_data->Y_data + loop_y) = y;
if (loop_y % 4 == 0) {
*(yuv_data->U_data + loop_uv) = u;
*(yuv_data->V_data + loop_uv) = v;
loop_uv++;
}
}break;
}
loop_y++; //Important!!!!!!!!!!!!!!!!
}
}
return true;
}
main.cpp
#include"BMP_file.h"
#include"YUV_raw.h"
#include"converter.h"
#include<windows.h>
#include<iostream>
using namespace std;
void process(BMP& img);
int main(int argc, char** argv)
{
BMP img(argv[1]);
img.show();
process(img);
system("Pause");
return 0;
}
void process(BMP& img)
{
int width = img.get_width();
int height = img.get_height();
chrominace_mode m = C420;
YUV_raw_image yuv_img(width, height, 1, m);
bmp2yuv(img, yuv_img);
yuv_img.save("./result.yuv");
}