How to efficently Read the Size of an JPEG Image on Stream

Viewed 336

Im programming a little add-on for our business application, the goal is to take pictures with a Barcodereader.

Everything works, but the problem is the Barcodereader sends the picture in intervals, they are pretty random (depends on the size of the image and the baud rate). Without full analysis of the bytes I receive there is no way to tell if picture is already loaded.

At the moment my logic tries to find start/end of the JPEG by searching for FF D8 and FF D9 bytes respectively. The problem is bytes FF D9 can occur inside image.

I obviously could do some specific analysis of the bytes, but as the Barcodereader continuously sends data, performing time consuming operations (debug, CPU, IO, etc) while receiving bytes will end up in a bluescreen.

What I exactly want is

  1. Reading the byte on which the size of the image is shown (I couldn't even research if the size will take the header itself / footer itself in consideration... do I have to calculate for that? ).

  2. Checking if the I received all bytes.

I will put the code of me receiving and working with the Bytes (its on a datareceived event, serialPortish) and a correct full Image in Bytes and and a corrupt image maybe that will help.

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

            }

Full ByteArray

https://codepen.io/NicolaiWalsemann/pen/KKzxaXg

Corrupt byte Array

https://codepen.io/NicolaiWalsemann/pen/YzqONxd

Solution 1

I could easily do a Timer which waits for 500ms-2000ms after the DataReceived event is first called. This would make sure that I have everything and then I can parse it as much as I want. But obviously always having to wait unreasonably is not what I want.

2 Answers

I think someone has already answered this: Detect Eof for JPG images

I couldn't say it any better.

Since you will be receiving chunks of data, you will need to parse as you go. Something like the following. It is untested and I may have the count calculation backwards (big vs little endian). It also assumes it's possible for a chunk to span 2 images or that the chunks may split FFxx codes and counts. It is also not optimized in any way, but for small images may be ok.

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

I haven't tested this, I don't know if it's right. But this is how I would approach the problem. Compared to the other answers there are a few considerations that I've tried to deal with;

  • Keep the byte array as a byte array
  • a single read event may contain partial chunks from multiple images
  • Assemble a whole image in a MemoryStream
  • Use the chunk length information to copy or skip the whole chunk

You might also wish to set the state back to 0 and stop copying an image if the memory buffer exceeds a maximum size. Or if no new header is encountered after the last one.

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