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);
}

 

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java摄像头通过v4l2采集YUYV格式的图像数据,可以通过以下步骤将其转换为YUV420P格式,然后再通过x26编码。 首先,我们需要了解YUYVYUV420P这两种图像数据格式的特点。 YUYV格式是一种16位的颜色编码格式,它使用两个连续的像素点共享一个色度信息(Cb和Cr),以减少颜色信息的采样率。每个像素所占用的字节为YUYVYUV420P格式是一种带有Y、Cb和Cr三个分量的图像格式,它采用了4:2:0的采样比例。对于每个Y像素,只有一个Cb和一个Cr分量,但对于每4个连续的Y像素,只有一个对应的Cb和一个对应的Cr分量。 将YUYV格式转换为YUV420P格式的步骤如下: 1. 首先,我们需要计算输出图像的分辨率(width和height)。假设输入图像的分辨率为inputWidth和inputHeight。 2. 创建一个大小为inputWidth * inputHeight * 3 / 2的字节数组,用来存储转换后的YUV420P数据。 3. 遍历输入图像的每个YUYV像素对(Y1, U, Y2, V),根据下面的公式计算输出图像的Y、Cb和Cr分量: Y1 = YUYV[2 * i]; U = YUYV[2 * i + 1]; Y2 = YUYV[2 * i + 2]; V = YUYV[2 * i + 3]; 对于输出图像的每个像素,计算对应的索引: outputIndex = (i / 2) * 3; 将Y、Cb和Cr分量写入输出图像的字节数组中: output[outputIndex] = Y1; output[outputIndex + 1] = U; output[outputIndex + 2] = V; output[outputIndex + 3] = Y2; output[outputIndex + 4] = U; output[outputIndex + 5] = V; 4. 最后,你可以使用x26编码器将转换后的YUV420P格式的图像数据进行编码。 这样,你就可以通过Java摄像头采集YUYV格式的图像数据,并将其转换为YUV420P格式,然后使用x26进行编码处理。 ### 回答2: 首先,Java可以使用v4l2库来采集摄像头的图像数据。v4l2是一个视频4 Linux 2 编程接口,允许开发者在Linux系统中访问和控制视频设备,包括摄像头。 在Java中,可以使用JNI(Java Native Interface)来使用v4l2库函数。通过JNI将Java程序与C/C++代码连接起来,实现通过v4l2采集yuyv格式的图像数据。 其次,要将yuyv格式的图像数据转换为yuv420p格式,可以使用算法来进行处理。yuyv格式是一种颜色编码格式,它包含了亮度和色度信息。而yuv420p格式是一种通用的图像格式,其中亮度和色度信息被分离成两个平面。 转换的算法可以根据yuyvyuv420p的颜色编码规则进行处理。例如,可以使用色度抽取和亮度补偿的算法来实现转换。具体的实现方式需要根据具体的编码规则进行相应的处理。 最后,将转换后的yuv420p格式的图像数据通过x264进行编码。x264是一个开源的H.264视频编码器库,可以将视频数据压缩为H.264格式。 通过调用x264库函数,可以将yuv420p格式的图像数据输入到x264编码器中进行编码。编码器会根据H.264的编码规则进行图像压缩,并输出压缩后的视频数据。 以上是关于在Java中使用v4l2采集yuyv格式图像数据,将其转换为yuv420p格式,再通过x264进行编码的基本流程的回答。具体的实现需要结合具体的编程环境和库函数使用来进行详细操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值