数组之后转成图片的处理"/>
得到摄像头一帧数据的字节数组之后转成图片的处理
最近做个伺服电机产品特性自动测试项目,首先要读取产品二维码以做生产管控和追溯,结果公司生产线上用的电脑和摄像头不支持原来的方案,具体原因不明确。于是试了七八个方案,用笔记本和其它品牌的摄像头都可以正确识别二维码,就是某品牌的摄像头不支持,没有办法只能决定调用AVICAP32读取摄像头的视频帧数据,然后自己转换成图片再来进行二维码识别。
在网上搜了很多贴子,发现网上用AVICAP32方案的贴子只有两种方案,一种方案是把截图保存到硬盘上再读出来,另一种方案是把截图数据复制到内存中的剪贴板,再去读取出来。这两种方案的特点是可以直接把帧视频数据转成24位bmp图片,然后用zxing开源代码就能进行二维码的解析了。虽然这两种方案都可以很好地解析出来二维码,但是对这个项目却不适用。
在这个项目中,需要不停地对流水线上的产品视频帧数据进行确认分析,解析出二维码之后如果工序正确就开始自动测试,根据测试结果区分产品,然后将良品流转到下一道工序,将不良品转到送往修理区的流水线。采用保存图片的第一种方案时,每300毫秒保存读取一次图片,不到两秒钟就显示内存不足,结果无法实现。采用第二种方案时,因为内存中的剪贴板数据是共用区域,主控计算机还要处理其它程序复制到剪贴板的共享数据,即使加了剪贴板数据类型是否bmp图片的判断,但实际效果仍是不理想,经常造成生产线其他生产程序错误,导致生产不能顺利进行。
网上很容易搜到用回调函数将一帧视频数据转到数组中方便使用的方法,但是接下来如何将这个数组转成bmp图片的介绍很少,如果不能正确解析这个数组,后面的工作也就没有办法继续下去了,所以这里就只说如何把这个数组转成bmp图片。其他的内容会在后面的代码中再做注释和说明。
假设已经能正确获得一帧视频数据的字节数组,发现该数组长度是614400字节,保存的截图是640*480像素大小,计算发现614400=640*480*2,可以推论每个像素保存的是2个字节的数据,2个字节16位bmp格式有很多,如RGB565,RGB555等,结果按这些格式转换之后图片的效果非常差,根本没有办法继续识别二维码。于是断定这个帧数据应该是属于YUV色彩空间。
网上也有很多YUV色彩空间这方面的介绍,主要编码方式有YUV4:4:4,YUV4:2:2以及YUV4:2:0,其中YUV4:4:4编码方式每个像素是3字节,YUV4:2:2编码方式每个像素是2字节,YUV4:2:0编码方式平均每个像素是1.5个字节。这里得到的一帧视频数组大小是每个像素占2个字节,因此判断应该是YUV4:2:2编码方式。
YUV4:2:2编码方式通常有3种存储方式,第一种Y1U1Y2V2;第二种U1Y1V2Y2;第三种YUV422P,就是先存储所有的Y,再存储U再存储V;于是3种方法都试一下,结果发现这个视频头所用的是第一种Y1U1Y2V2存储方式;接下来就很容易完成图片处理,二维码解析等动作了。程序运行下来每300毫秒检测识别一次二维码,识别出来之后暂停流水线开始自动测试,测试完毕启动流水线继续检测等待下一个产品到来。因为是在同一个程序中处理视频数据检测二维码,不会对其他程序造成干扰,至此问题解决。
接下来附上代码并进行说明:
byte[] snapbmpdata = webcam.OneFrameData;
if (snapbmpdata != null)
{
//调试的时候用
//定时器01停止截图
if (_01timer != null && _01timer.Enabled)
{
_01timer.Stop();
_01timer.Dispose();
}
Bitmap bitmap = new Bitmap(640, 480, PixelFormat.Format24bppRgb);
Rectangle rect = new Rectangle(0, 0, vedioWidth, vedioHeight);
//以读写方式锁定位图
System.Drawing.Imaging.BitmapData bmpData =
bitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
//首地址
IntPtr m_ip = bmpData.Scan0;
//计算16位bmp的扫描宽度
int snapStride = vedioWidth * (16 / 8);
int camstride = snapStride % 4;
if (camstride != 0)
snapStride = snapStride + (4 - camstride);
int snapBytes = snapStride * vedioHeight;
//计算24位bmp的扫描宽度
int bmpStride = vedioWidth * (24 / 8);
camstride = bmpStride % 4;
if (camstride != 0)
bmpStride = bmpStride + (4 - camstride);
int bmpBytes = bmpStride * vedioHeight;
byte[] newbmpdata = new byte[bmpBytes];
double colorY01 = 0;
double colorU01 = 0;
double colorV01 = 0;
double colorY02 = 0;
double colorU02 = 0;
double colorV02 = 0;
double colorRed01 = 0;
double colorGreen01 = 0;
double colorBlue01 = 0;
double colorRed02 = 0;
double colorGreen02 = 0;
double colorBlue02 = 0;
//把视频帧数组的像素信息转成24位图片信息
//编码格式估计是YUV4:2:2格式
//每相邻两个像素存储4个字节
//存储方式有可能是采用YUV422P模式
for (int i = 0; i < vedioHeight; i++)
{
//每次+6表示水平方向两个像素点一起处理
for (int j = 0; j < snapStride; j += 4)
{
//对于视频流来说对应的是YUV前两点,计算其YUV分量
//存储方式有YUYV和UYUV两种相邻方式
colorY01 = snapbmpdata[i * snapStride + j ];
colorU01 = snapbmpdata[i * snapStride + j + 1];
colorY02 = snapbmpdata[i * snapStride + j + 2];
colorV02 = snapbmpdata[i * snapStride + j + 3];
colorV01 = colorV02;
colorU02 = colorU01;
colorRed01 = colorY01 + (1.370705 * (colorV01 - 128));
colorGreen01 = colorY01 - (0.698001 * (colorV01 - 128)) - (0.337633 * (colorU01 - 128)); ;
colorBlue01 = colorY01 + (1.732446 * (colorU01 - 128));
colorRed02 = colorY02 + (1.370705 * (colorV02 - 128));
colorGreen02 = colorY02 - (0.698001 * (colorV02 - 128)) - (0.337633 * (colorU02 - 128)); ;
colorBlue02 = colorY02 + (1.732446 * (colorU02 - 128));
if (colorRed01 > 255)
colorRed01 = 255;
if (colorGreen01 > 255)
colorGreen01 = 255;
if (colorBlue01 > 255)
colorBlue01 = 255;
if (colorRed01 < 0)
colorRed01 = 0;
if (colorGreen01 < 0)
colorGreen01 = 0;
if (colorBlue01 < 0)
colorBlue01 = 0;
if (colorRed02 > 255)
colorRed02 = 255;
if (colorGreen02 > 255)
colorGreen02 = 255;
if (colorBlue02 > 255)
colorBlue02 = 255;
if (colorRed02 < 0)
colorRed02 = 0;
if (colorGreen02 < 0)
colorGreen02 = 0;
if (colorBlue02 < 0)
colorBlue02 = 0;
int bmpheightindex = vedioHeight - 1 - i;
int bmpwidthindex = 6 * ( j / 4 );
newbmpdata[bmpheightindex * bmpStride + bmpwidthindex] = (byte)colorRed01;
newbmpdata[bmpheightindex * bmpStride + bmpwidthindex + 1] = (byte)colorGreen01;
newbmpdata[bmpheightindex * bmpStride + bmpwidthindex + 2] = (byte)colorBlue01;
newbmpdata[bmpheightindex * bmpStride + bmpwidthindex + 3] = (byte)colorRed02;
newbmpdata[bmpheightindex * bmpStride + bmpwidthindex + 4] = (byte)colorGreen02;
newbmpdata[bmpheightindex * bmpStride + bmpwidthindex + 5] = (byte)colorBlue02;
}
}
//复制回位图
System.Runtime.InteropServices.Marshal.Copy(newbmpdata, 0, m_ip, bmpBytes);
bitmap.UnlockBits(bmpData);
bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
this.pictureBox1.Image = ResizeImage(bitmap, pictureBox1);
this.pictureBox1.Refresh();
更多推荐
得到摄像头一帧数据的字节数组之后转成图片的处理
发布评论