如何解决如何有效地读取流上JPEG图像的大小
我为我们的业务应用程序编写了一些附加程序,目的是使用 Barcodereader 拍照。
一切正常,但是问题是条形码阅读器每隔一段时间发送一次图片,它们是随机的(取决于图片的大小和波特率)。如果没有完整分析我收到的字节,就无法判断图片是否已加载。
此刻,我的逻辑尝试通过分别搜索FF D8
和FF D9
字节来查找JPEG的开始/结束。问题是图像内部可能出现FF D9
个字节。
我显然可以对字节进行一些特定的分析,但是随着Barcodereader不断发送数据,执行耗时的操作(调试,cpu,IO等),而接收字节的操作最终会以 bluescreen >。
我真正想要的是
-
读取显示图像大小的字节(我无法 甚至研究大小是否会包含页眉本身/页脚 本身正在考虑...我必须为此计算吗? )。
-
检查我是否收到了所有个字节。
我将把接收和使用字节的代码(在发生数据接收的事件,serialPortish上)和正确的完整映像(以字节为单位)放在一个字节中,并且损坏的映像也许会有所帮助。
DataReceivedEvent
private void ScannerPort_DataReceived(object sender,DataReceivedEventArgs e)
{
if (_WaitingForImage)
{
List<byte> imageBufferList = new List<byte>(e.RawBuffer);
{
if (imageBufferList[0] == 0x24 && imageBufferList[1] == 0x69)
{
for (int i = 0; i < 17; i++)
{
imageBufferList.RemoveAt(0);
}
}
byte[] imageBuffer = imageBufferList.ToArray();
_ImageReceiving = false;
_WaitingForImage = false;
this.OnImageReceived(imageBuffer);
}
//imageBufferList.AddRange(e.RawBuffer);
}
完整字节数组
https://codepen.io/NicolaiWalsemann/pen/KKzxaXg
损坏的字节数组
https://codepen.io/NicolaiWalsemann/pen/YzqONxd
解决方案1
我可以轻松地创建一个计时器,该计时器在首次调用 DataReceived 事件后等待500ms-2000ms。这样可以确保我拥有所有内容,然后我可以根据需要进行尽可能多的解析。但是显然,总是不合理地等待并不是我想要的。
解决方法
我认为有人已经回答了这个问题:Detect Eof for JPG images
我不能再说了。
由于您将接收大量数据,因此您将需要在运行时进行解析。类似于以下内容。它未经测试,我可能会向后计算计数(大字节序与小字节序)。它还假定一个块有可能跨越2个图像,或者这些块可以拆分FFxx代码和计数。它也没有进行任何优化,但是对于较小的图像可能没问题。
private List<byte> imageBuffer = new List<byte>();
private int imageState = 0;
private int skipBytes = 0;
private void ScannerPort_DataReceived(object sender,DataReceivedEventArgs e)
{
List<byte> tempBuffer = new List<byte>(e.RawBuffer);
foreach (byte b in tempBuffer)
{
_ImageReceiving = true;
imageBuffer.Add(b);
switch (imageState)
{
case 0: // Searching for FF
if(b == 0xFF)
imageState = 1;
break;
case 1: // First byte after FF
if (b == 0 || b == 1 || (b <= 0xD8 && b >= 0xD1))
{
// Code is not followed by a count
imageState = 0;
}
else if (b == 0xD9)
{
// End of image
_ImageReceiving = false;
this.OnImageReceived(imageBuffer.ToArray());
imageBuffer = new List<byte>();
imageState = 0;
}
else
{
// Code is
imageState = 2;
}
break;
case 2: // First count byte,big endian?
skipBytes = ((int) b) * 0x100;
imageState = 3;
break;
case 3: // Second count byte
skipBytes += b;
imageState = 4;
break;
case 4: // skip
skipBytes--;
if (skipBytes == 0) imageState = 0;
break;
}
}
}
,
我还没有测试过,我不知道它是否正确。但这就是我要解决的问题。与其他答案相比,我尝试处理一些注意事项;
- 将字节数组保留为字节数组
- 单个读取事件可能包含来自多个图像的部分块
- 在
MemoryStream
中组装整个图像 - 使用块长度信息复制或跳过整个块
如果内存缓冲区超过最大大小,您可能还希望将状态设置回0并停止复制图像。或者,如果在最后一个之后没有遇到新的标头。
private MemoryStream image = new MemoryStream();
private int state = 0;
private bool copy = false;
private int blockLen = -1;
private void ImageReceived(Stream image) {
// TODO use whole image buffer
}
private void Received(byte[] block)
{
var i = 0;
while (i < block.Length)
{
if (state == 4 && blockLen > 0)
{
var remaining = block.Length - i;
if (remaining > blockLen)
remaining = blockLen;
if (copy)
image.Write(block,i,remaining);
i += remaining;
blockLen -= remaining;
if (blockLen <= 0)
state = 0;
}
else
{
var b = block[i++];
switch (state)
{
case 0:
if (b == 0xFF)
state = 1;
break;
case 1:
if (b == 0xD8) { // SOI
copy = true;
image.Seek(0,SeekOrigin.Begin);
image.SetLength(0);
image.WriteByte((byte)0xFF); // the first byte that we already skipped
} else if (b == 0xD9) { // EOI
if (copy)
{
image.WriteByte(b);
image.Seek(0,SeekOrigin.Begin);
ImageReceived(image);
}
copy = false;
state = 0;
} else if (b == 0xFF) { // NOOP
} else if ((b & 0xF8) == 0xD0) { // RSTn
// You could verify that n cycles from 0-7
state = 0;
} else {
state = 2;
}
break;
case 2:
blockLen = b << 8;
state = 3;
break;
case 3:
// length includes the 2 length bytes,which we've just skipped
blockLen = (blockLen | b) -2;
state = 4;
break;
}
if (copy)
image.WriteByte(b);
}
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。