V4L2读取摄像头YUYV(YUV420)帧后使用C语言转存为bmp格式

  • 摄像头配置
  • 读取一帧YUV420(YUYV)
  • 保存为RGB24图像(BRG的顺序,bmp)
  • 下面是内存中摄像头读取的数据直接转存为RGB图片的源码。
  • 输入:图像指针地址,图像长度(两个参数都能由V4L2读取一帧的时候获得)
  • 下面是源码,读取一张yuyv的图之后转存为bmp(BGR24和灰度)
  • 注意,核心都是C语言实现的。opencv只是用来show检查转换是否正确。
  • 测试的yuyv的图片大小是640*480,它实际大小是640*480*2*8bit。而转为RGB24之后,占用的空间是640*480*3*8bit。BMP使用的是BGR的顺序。
  • yuyv的格式说明参见http://www.fourcc.org/yuv.php#YUYV

// tmp.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
// yuyv (yuy2) to RGB24(bgr顺序)
// test passed  
// ref http://www.fourcc.org/yuv.php#YUYV


#include <iostream>
#include "opencv2/core.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/opencv.hpp"
#include <stdio.h>
#include <stdlib.h>

#pragma comment(lib,"opencv_world430d.lib")

using namespace cv;
using namespace std;


void yuyv2bgr(unsigned char *p_yuyv, unsigned char * bgr_image, int size);//yuyv to bgr

void save_bmp_bgr(uchar * pdata, const char * bmp_file, int width, int height); // 24bit BGR24 save to bmp

void save_bmp_gray(unsigned char *inputImg, int width, int height, const char *outputFileName);// gray scale
// char 一行一行拉直 3byte或者1byte


int main()
{
	std::cout << "Hello World!\n";
	FILE *binaryf = fopen("H:\\0communicate\\my0630.yuyv", "rb");
	unsigned char* bytesFromBinary = new unsigned char[640 * 480 * 2];
	unsigned char* bgr_image = new unsigned char[640 * 480 * 3];

	fread(bytesFromBinary, 1, 640 * 480 * 2, binaryf);
	fclose(binaryf);
	yuyv2bgr(bytesFromBinary, bgr_image, 640 * 480 * 2);
	save_bmp_gray(bgr_image,640,480,"filename.bmp"); // 出错,需要输入灰度图
	const char * fn= "BGR.bmp";
	save_bmp_bgr(bgr_image, fn, 640, 480);


	delete[] bytesFromBinary;
	delete[] bgr_image;

}

//static unsigned char   *bgr_image;

void yuyv2bgr(unsigned char *p_yuyv, unsigned char * bgr_image, int size)
{

	int i;
	float y1, y2, u, v;
	unsigned char * bgr_p = bgr_image;
	unsigned char * p_tmp = (unsigned char *)p_yuyv;

	for (i = 0; i < size; i += 4)
	{

		y1 = p_tmp[i];
		u = p_tmp[i + 1];
		y2 = p_tmp[i + 2];
		v = p_tmp[i + 3];

		bgr_p[0] = (y1 + 1.371*(u - 128.0));
		bgr_p[1] = (y1 - 0.698*(u - 128.0) - 0.336*(v - 128.0));
		bgr_p[2] = (y1 + 1.732*(v - 128.0));
		// 如果要灰度图,在此处处理 要更改rgb顺序,在此处修改
		bgr_p[3] = (y2 + 1.371*(v - 128.0));
		bgr_p[4] = (y2 - 0.698*(v - 128.0) - 0.336*(u - 128.0));
		bgr_p[5] = (y2 + 1.732*(u - 128.0));
		// 合到一起用para for todo 2020年7月1日
		bgr_p += 6;
	}
	int row = 480;
	int col = 640;
	Mat im = Mat(row, col, CV_8UC3, bgr_image);
	imshow("BGR", im);
	imwrite("byopencv.jpg", im);
	if (im.isContinuous())
	{
		cout << im.channels()<<endl;
		const char *fname = "bmp2.bmp";
		save_bmp_bgr(im.data, fname,640,480 );// 
	}
	else
		cout << "Not Continuous" << endl;
	//cvtColor(im, im, COLOR_BGR2RGB);
	cvtColor(im, im, COLOR_BGR2GRAY);
	if (im.isContinuous())
	{
		cout << im.channels();
		save_bmp_gray(im.data, 640, 480, "bmp1.bmp");// 正确
		/*const char *fname = "bmp3.bmp";
		save_bmp_bgr(im.data, fname, 640, 480);*/
	}
	waitKey(0);
}


void write_bmpheader(unsigned char *bitmap, int offset, int bytes, int value)
{
	int i;
	for (i = 0; i < bytes; i++)
		bitmap[offset + i] = (value >> (i << 3)) & 0xFF;
}

unsigned char *convertToBmp(unsigned char *inputImg, int width, int height, int *ouputSize)
{
	/*create a bmp format file*/
	int bitmap_x = (int)ceil((float)width * 3 / 4) * 4;
	unsigned char *bitmap = (unsigned char*)malloc(sizeof(unsigned char)*height*bitmap_x + 54);

	bitmap[0] = 'B';
	bitmap[1] = 'M';
	write_bmpheader(bitmap, 2, 4, height*bitmap_x + 54); //whole file size
	write_bmpheader(bitmap, 0xA, 4, 54); //offset before bitmap raw data
	write_bmpheader(bitmap, 0xE, 4, 40); //length of bitmap info header
	write_bmpheader(bitmap, 0x12, 4, width); //width
	write_bmpheader(bitmap, 0x16, 4, height); //height
	write_bmpheader(bitmap, 0x1A, 2, 1);//biPlanes
	write_bmpheader(bitmap, 0x1C, 2, 24); //bit per pixel 24
	write_bmpheader(bitmap, 0x1E, 4, 0); //compression
	write_bmpheader(bitmap, 0x22, 4, height*bitmap_x); //size of bitmap raw data
	for (int i = 0x26; i < 0x36; i++)//0x36 54 //0x26 38
		bitmap[i] = 0;

	int k = 54;
	for (int i = height - 1; i >= 0; i--) 
	{
		int j;
		for (j = 0; j < width; j++) 
		{
			int index = i * width + j;//index 只到了width*height-1 so 只能保存灰度图。如果要彩色的,直接将imgdatacopy到bitmap中就行
			// 如果只是保存灰度图,那么bit per pixel 24写成8就行。说明比特数/像素数,值有1、2、4、8、16、24、32;
			for (int l = 0; l < 3; l++)
			{ 
				bitmap[k++] = inputImg[index];
			}
		}
		j *= 3;
		while (j < bitmap_x) //width*3
		{
			bitmap[k++] = 0;
			j++;
		}
	}

	*ouputSize = k;
	return bitmap;
}

void save_bmp_gray(unsigned char *inputImg, int width, int height, const char *outputFileName)
{
	int size;
	unsigned char *bmp = convertToBmp(inputImg, width, height, &size);
	FILE *fp = fopen(outputFileName, "wb+");
	if (fp == NULL)
	{
		printf("Could not open file.\n");
	}
	fwrite(bmp, 1, size, fp);
	fclose(fp);
	free(bmp);
}
//--------------------

typedef long LONG;//4
typedef unsigned int DWORD;//4
typedef unsigned short WORD;//2

typedef struct {
	WORD    bfType;
	DWORD   bfSize;
	WORD    bfReserved1;
	WORD    bfReserved2;
	DWORD   bfOffBits;
} BMPFILEHEADER_T; // sizeof  = 16 注意了,此处有坑。

typedef struct {
	DWORD      biSize;
	LONG       biWidth;
	LONG       biHeight;
	WORD       biPlanes;
	WORD       biBitCount;
	DWORD      biCompression;
	DWORD      biSizeImage;
	// 接下来的填0可以
	LONG       biXPelsPerMeter;
	LONG       biYPelsPerMeter;
	//biClrUsed:说明位图使用的调色板中的颜色索引数,为0说明使用所有;
	//	biClrImportant:说明对图像显示有重要影响的颜色索引数,为0说明都重要;
	DWORD      biClrUsed;
	DWORD      biClrImportant;
} BMPINFOHEADER_T;

void save_bmp_bgr(uchar * pdata, const char * bmp_file, int width, int height)
{      //分别为rgb数据,要保存的bmp文件名,图片长宽 无调色板
	int size = width * height * 3 * sizeof(char); // 每个像素点3个字节
	// 位图第一部分,文件信息
	BMPFILEHEADER_T bfh;
	bfh.bfType = (WORD)0x4d42;  //bm unsigned short bfType=0x4d42;  
	bfh.bfSize = size  // data size
		+ sizeof(BMPFILEHEADER_T) // first section size
		+ sizeof(BMPINFOHEADER_T) // second section size
		;
	bfh.bfReserved1 = 0; // reserved
	bfh.bfReserved2 = 0; // reserved
	//bfh.bfOffBits = sizeof(BMPFILEHEADER_T) + sizeof(BMPINFOHEADER_T);//真正的数据的位置 注意了,此处有坑 2020年7月1日
	bfh.bfOffBits = 54;// 14 + 40, 前一行的结论是56

	// 位图第二部分,数据信息
	BMPINFOHEADER_T bih;
	bih.biSize = sizeof(BMPINFOHEADER_T); //biSize:4字节,信息头的大小,即40;
	bih.biWidth = width; //biWidth:4字节,以像素为单位说明图像的宽度;
	bih.biHeight = -height;//BMP图片从最后一个点开始扫描,显示时图片是倒着的,所以用-height,这样图片就正了
	bih.biPlanes = 1;//为1,不用改
	bih.biBitCount = 24;// 24位一个像素
	bih.biCompression = 0;//不压缩
	bih.biSizeImage = size;//图像数据的byte个数
	bih.biXPelsPerMeter = 2835;//像素每米
	bih.biYPelsPerMeter = 2835;//像素每米
	bih.biClrUsed = 0;//已用过的颜色,24位的为0
	bih.biClrImportant = 0;//每个像素都重要
	FILE * fp = fopen(bmp_file, "wb");
	if (!fp) return;

	//fwrite(&bfh, 8, 1, fp);//由于linux上4字节对齐,而信息头大小为54字节,第一部分14字节,第二部分40字节,所以会将第一部分补齐为16字节,直接用sizeof,打开图片时就会遇到premature end-of-file encountered错误
	//fwrite(&bfh.bfReserved2, sizeof(bfh.bfReserved2), 1, fp);
	//fwrite(&bfh.bfOffBits, sizeof(bfh.bfOffBits), 1, fp);
	cout << sizeof(LONG) << endl;//4
	cout << sizeof(WORD) << endl;//2
	cout << sizeof(DWORD) << endl;//4

	cout << sizeof(BMPFILEHEADER_T) << endl;//16
	cout << sizeof(BMPINFOHEADER_T) << endl;//40

	fwrite(&bfh, 8, 1, fp);//由于linux上4字节对齐,而信息头大小为54字节,第一部分14字节,第二部分40字节,所以会将第一部分补齐为16直接,直接用sizeof,打开图片时就会遇到premature end-of-file encountered错误
	fwrite(&bfh.bfReserved2, sizeof(bfh.bfReserved2), 1, fp); //2
	fwrite(&bfh.bfOffBits, sizeof(bfh.bfOffBits), 1, fp); //4
	fwrite(&bih, sizeof(BMPINFOHEADER_T), 1, fp); //40
 

	fwrite(pdata, size, 1, fp);
	fclose(fp);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值