- 摄像头配置
- 读取一帧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);
}