2

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.

eocron
  • 6,885
  • 1
  • 21
  • 50
  • I probably sould explain the Loop in there, our Barcode Scanner sends after a Pic is made, a "confirmation" that a picture will now be sent. its 17 bytes long. thats the Remove loop there. but that has nothing to do with the picture itself... – Nico Walsemann Sep 18 '20 at 05:47
  • Maybe the answers here will help: [Getting Image size of JPEG from its binary](https://stackoverflow.com/questions/2517854/getting-image-size-of-jpeg-from-its-binary) – cheznoid Sep 20 '20 at 22:42
  • For example, it seems that some scanner vendors have APIs and libraries, but is it possible that you are using such a vendor? [Image capturing via SNAPI.dll API for Symbol barcode scanner DS4208](https://stackoverflow.com/q/58562096/9014308) – kunif Sep 21 '20 at 12:45
  • Why do you need `_WaitingForImage` guard? This may ignore some next event handler call and you will loose the next received buffer. Probably it may contain some useful data. Look at **Producer-Consumer** programming pattern implementations, it may help to understand how to implement it correctly. E.g. using `BlockingCollection` of received buffers. Is barcode scanner guarantees that whole image will be sent at once or it can be split into portions? You must merge the portions into single array then. – aepot Sep 23 '20 at 10:16
  • @aepot i have cut down the code, to where only important stuff is done, normaly there is a serialization of barcodes there, witch is turned of by _WaitingForImage. As i manually have to set the Scanner into Image mode, and the scanner is not reading smth else. And WaitingForImage will ONLY put into False when a full image is received, and in no other way. So it does actually nothing in this problem. – Nico Walsemann Sep 24 '20 at 14:51

2 Answers2

2

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;
            }
        }
    }
Tim
  • 606
  • 4
  • 7
  • I have read the Post througly, but there simply starts the Problem. If i would be able instantly receive the Full Image. and Parse it through. there isnt even a Problem here. Its that the Barcode Reader sends bytes in a random intervall in random amount of intervalls. And doing any "big" parsing except is byte x = y will take too long to process, and the Scanner will have problem sending data (i knew the specific reason for it thats just a fast explaination) i need a way, in runtime without ANY loops to know when this whole thing is finished... – Nico Walsemann Sep 24 '20 at 14:54
  • I"m not sure how you can process a buffer with an unknown length without any loops given the structure of jpg and the requirement that the data may be split into arbitrary chunks, even potentially splitting markers or counts. I've updated the answer with one possible solution, though unoptimized and untested. – Tim Sep 25 '20 at 01:20
  • 1) don't add a byte array to a list. 2) don't count the bytes you are skipping one by one. 3) assemble the chunks of image to a MemoryStream or something. – Jeremy Lakeman Sep 25 '20 at 01:36
  • Still not good solution. OP obviously perform pattern matching in stream of data, so in case of error, you should try another byte after first one feeded to automata (you save queue starting from first byte in autotamata to end of potential match). If you don't do this, you will lose result - FF D8 FF D8 – eocron Sep 25 '20 at 08:08
2

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);
        }
    }
}
Jeremy Lakeman
  • 9,515
  • 25
  • 29