4

I am struggling to successfully decode a JPEG image from bytes, back to JPEG again.

I started from encoded frame from a MJPG bytes stream, which I want to decode in order to manipulate with OpenCV. I am a bit of a newbie at Python, numpy, opencv etc!

I now have the frame JPG data in a text file as: b'\xf\xd8\xff\xdb\x00....etc etc for purposes of testing:

code seems to fail when I try to Resize the numpy array to the original video stream resolution (640, 480) on line 14 (npFlat.reshape((640,480))

**ValueError: cannot reshape array of size 228140 into shape (640,480)*

import io
import cv2
import numpy as np

BytesFile = open('FrameBytes.txt')
MyBytes=BytesFile.read()
BytesFile.close()

dt=np.dtype(np.unit8)
dt=dt.newbtyeorder('>')

npFlat = np.fromfile('FrameBytes.txt'.dtype=dt)
npResized = npFlat.reshape(640,480,3) #CODE FAILING TO RESIZE AT THIS LINE...
cv.imshow('resized',npResized)

Could it be that even though my video frame was captured from a 640, 480 feed, for some reason during encoding the size has changed? This is all I can think of at the moment. Any/all help welcome.

I have reviewed a related post:Python - byte image to NumPy array using OpenCV but trying to avoid PIL, and the frombuffer method also seems to be failing for me.

Ok, so I made some progress and now have:

npFlat = np.frombuffer(MyBytes.encode('utf-8'),dtype=np.int8).

I can now also get the code to succeed when I 'reshape' npFlat to (374, 610). I.e. so that 374 x 610 = the flat numpy array, which is of length 228140...but this all seems odd. the buffer information represents a JPG which I am trying to reopen...and am not getting close yet.

MyBytes.txt" Data Input File is viewable here: https://drive.google.com/file/d/18pqILl9myeTRjdiqtExFyOe94Km_aNNM/view?usp=sharing]1

CodeFlan
  • 105
  • 1
  • 8
  • Hmm it's becasuse 640*480*3 != 228140. By resizing do you mean reshaping or resampling? – tstanisl Jun 12 '20 at 17:50
  • reshaping - I have added more info to my original post. I am trying to decode a JPG from a buffer (known frame) to open and edit with opencv... – CodeFlan Jun 12 '20 at 21:43
  • Your`MyBytes` object have the actual JPG file bytes - that must be first decoded to in-memry representation of pixel-data, using, for example, PIL, before it can be treated as an image in Python code. – jsbueno Jun 12 '20 at 21:48
  • Please post your actual code! I'm fairly certain `import nump as np` doesn't run. Nor does `newbtyorder`. Also, please share your input file using Dropbox or GoogleDrive or somesuch. Thank you. – Mark Setchell Jun 12 '20 at 22:05
  • Thanks Mark will get back tomorrow eve. Typo amended on the numpy import above - i was struggling with copying my actual code across VNC. newbyteorder i am not using now. Seems unneccessary. – CodeFlan Jun 12 '20 at 23:43
  • Please post your ACTUAL code! You keep posting rough approximations of your code so people keep wasting their time figuring out what is an actual issue and what is just a typo. This line is incorrect - it has a period (full stop) rather than a comma `npFlat = np.fromfile('FrameBytes.txt'.dtype=dt)` This line is incorrect `dt=np.dtype(np.unit8)` - there is no `unit8` type. – Mark Setchell Jun 13 '20 at 19:43

2 Answers2

4

You have made quite a mess - you should avoid saving binary data as text files!

Copy the contents of your file into your clipboard - on a Mac the command is:

cat frame.txt | pbcopy

Start Python and make a variable called s and set it to the contents of the clipboard:

s = PASTE_YOUR_CLIPBOARD

Now do:

from PIL import Image
from io import BytesIO

# Load image from BytesIO
im = Image.open(BytesIO(s))

# Display image and save image
im.show()
im.save('result.png')

If you are on OpenCV, use:

import cv2

# Make s as above
s = PASTE_YOUR_CLIPBOARD

i = np.frombuffer(s,dtype=np.uint8)

im = cv2.imdecode(i,cv2.IMREAD_UNCHANGED)

cv2.imwrite('result.png',im)

enter image description here

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • Mark - thanks! I assumed it was the downstream 'frombuffer' imdecode bit which was failing in my (OpenCV) approach. Thanks for confirming this was not the case! – CodeFlan Jun 13 '20 at 20:37
  • Purely from an academic perspective, would you be able to share how should one should save binary data to file, and import (I.e. NOT text file! :-) )? – CodeFlan Jun 13 '20 at 20:38
  • 1
    If you have an image in a Numpy array, you can just use `np.save('frame.npy', image)` like this... https://numpy.org/doc/1.18/reference/generated/numpy.save.html – Mark Setchell Jun 14 '20 at 07:28
  • Thanks. I've learnt a lot from this experience and my original post attempted to do something the incorrect way, using an incorrect data input! - My issue is now resolved and I can work on finding my next problem! – CodeFlan Jun 15 '20 at 14:49
0

Your FrameBytes.txt file, despite the extension is really a JPG file (unless you garbled it on saving anyway).

So, you have t read it with a proper image reader, not as bytes, if you want to manipulate it as an image. Otherwise, you have the JPEG encoded bytes - your 228140 bytes on disk represent an image with 921600 bytes of data when decompressed. (which is reasonable for a high-quality jpeg file).

Simply use PIL for that:


from PIL import Image

img = Image.open("FrameBytes.txt")
print(img.size) # this should print (640, 480).

# to convert it to a numpy array:
import numpy as np
data = np.array(list(img.tobytes()), dtype="uint8")
data.shape = img.size + (3,)

...

If you don't want to use PIL, of course,you can use other libraries - opencv itself have the cv2.imread method that should work out of the box.

jsbueno
  • 99,910
  • 10
  • 151
  • 209
  • Hi jsbueno - I get: OS Error: cannot identify image file 'FrameBytes.txt' when I try to open using Image.open(). Odd - I was confident the bytes in the text file represent a frame I outputted from a MJPEG stream. – CodeFlan Jun 12 '20 at 23:01
  • Yes, but I included the comment "unless you garbled it on saving". You were oppeining the file incorrecly as a text file. As you don't describe the process or code you usd when saving the file, there is no way I can guess. If you are on Mac or Linux try running the "file" shell command to identify the file. On Windows, change the file extension to ".jpg" instead of "txt" - although that should not interfere with PIL identifying it. – jsbueno Jun 13 '20 at 00:29
  • using Linux Shell file command, returns: "ASCII text, with very long lines, with no line terminators". Origin of file is: I printed out a JPG frame as bytes to python terminal, then copy-pasted into a text file. This was intended for me to practice and test the method of converting the bytes to an openCV image to work on a processing program, and then encode back to bytes for streaming onwards inside another working video streaming application that I outputted the bytes from. I will share the file shortly. – CodeFlan Jun 13 '20 at 18:10