This is what I normally use to convert images stored in database to OpenCV images in Python.
import numpy as np
import cv2
from cv2 import cv
# Load image as string from file/database
fd = open('foo.jpg','rb')
img_str = fd.read()
fd.close()
# CV2
nparr = np.fromstring(img_str, np.uint8)
img_np = cv2.imdecode(nparr, cv2.CV_LOAD_IMAGE_COLOR) # cv2.IMREAD_COLOR in OpenCV 3.1
# CV
img_ipl = cv.CreateImageHeader((img_np.shape[1], img_np.shape[0]), cv.IPL_DEPTH_8U, 3)
cv.SetData(img_ipl, img_np.tostring(), img_np.dtype.itemsize * 3 * img_np.shape[1])
# check types
print type(img_str)
print type(img_np)
print type(img_ipl)
I have added the conversion from numpy.ndarray to cv2.cv.iplimage, so the script above will print:
<type 'str'>
<type 'numpy.ndarray'>
<type 'cv2.cv.iplimage'>
EDIT:
As of latest numpy 1.18.5 +, the np.fromstring raise a warning, hence np.frombuffer shall be used in that place.
This is what I normally use to convert images stored in database to OpenCV images in Python.
import numpy as np
import cv2
from cv2 import cv
# Load image as string from file/database
fd = open('foo.jpg','rb')
img_str = fd.read()
fd.close()
# CV2
nparr = np.fromstring(img_str, np.uint8)
img_np = cv2.imdecode(nparr, cv2.CV_LOAD_IMAGE_COLOR) # cv2.IMREAD_COLOR in OpenCV 3.1
# CV
img_ipl = cv.CreateImageHeader((img_np.shape[1], img_np.shape[0]), cv.IPL_DEPTH_8U, 3)
cv.SetData(img_ipl, img_np.tostring(), img_np.dtype.itemsize * 3 * img_np.shape[1])
# check types
print type(img_str)
print type(img_np)
print type(img_ipl)
I have added the conversion from numpy.ndarray to cv2.cv.iplimage, so the script above will print:
<type 'str'>
<type 'numpy.ndarray'>
<type 'cv2.cv.iplimage'>
EDIT:
As of latest numpy 1.18.5 +, the np.fromstring raise a warning, hence np.frombuffer shall be used in that place.
I think this answer provided on this stackoverflow question is a better answer for this question.
Quoting details (borrowed from @lamhoangtung from above linked answer)
import base64
import json
import cv2
import numpy as np
response = json.loads(open('./0.json', 'r').read())
string = response['img']
jpg_original = base64.b64decode(string)
jpg_as_np = np.frombuffer(jpg_original, dtype=np.uint8)
img = cv2.imdecode(jpg_as_np, flags=1)
cv2.imwrite('./0.jpg', img)
I created a 2x2 JPEG image to test this. The image has white, red, green and purple pixels. I used cv2.imdecode and numpy.frombuffer
import cv2
import numpy as np
f = open('image.jpg', 'rb')
image_bytes = f.read() # b'\xff\xd8\xff\xe0\x00\x10...'
decoded = cv2.imdecode(np.frombuffer(image_bytes, np.uint8), -1)
print('OpenCV:\n', decoded)
# your Pillow code
import io
from PIL import Image
image = np.array(Image.open(io.BytesIO(image_bytes)))
print('PIL:\n', image)
This seems to work, although the channel order is BGR and not RGB as in PIL.Image. There are probably some flags you might use to tune this. Test results:
OpenCV:
[[[255 254 255]
[ 0 0 254]]
[[ 1 255 0]
[254 0 255]]]
PIL:
[[[255 254 255]
[254 0 0]]
[[ 0 255 1]
[255 0 254]]]
I searched all over the internet finally I solved:
NumPy array (cv2 image) - Convert
NumPy to bytes
and
bytes to NumPy
:.
#data = cv2 image array
def encodeImage(data):
#resize inserted image
data= cv2.resize(data, (480,270))
# run a color convert:
data= cv2.cvtColor(data, cv2.COLOR_BGR2RGB)
return bytes(data) #encode Numpay to Bytes string
def decodeImage(data):
#Gives us 1d array
decoded = np.fromstring(data, dtype=np.uint8)
#We have to convert it into (270, 480,3) in order to see as an image
decoded = decoded.reshape((270, 480,3))
return decoded;
# Load an color image
image= cv2.imread('messi5.jpg',1)
img_code = encodeImage(image) #Output: b'\xff\xd8\xff\xe0\x00\x10...';
img = decodeImage(img_code) #Output: normal array
cv2.imshow('image_deirvlon',img);
print(decoded.shape)
You can get full code from here
How to convert byte array to OpenCV Mat in Python - Stack Overflow
[Help!] How to encoded byte array to Mat??
How to read raw png from an array in python opencv? - Stack Overflow
Python OpenCV convert image to byte string? - Stack Overflow
Updated Again
I looked into JPEG decoding from the memory buffer using PyTurboJPEG, the code goes like this to compare with OpenCV's imdecode():
#!/usr/bin/env python3
import cv2
from turbojpeg import TurboJPEG, TJPF_GRAY, TJSAMP_GRAY
# Load image into memory
r = open('image.jpg','rb').read()
inp = np.asarray(bytearray(r), dtype=np.uint8)
# Decode JPEG from memory into Numpy array using OpenCV
i0 = cv2.imdecode(inp, cv2.IMREAD_COLOR)
# Use default library installation
jpeg = TurboJPEG()
# Decode JPEG from memory using turbojpeg
i1 = jpeg.decode(r)
cv2.imshow('Decoded with TurboJPEG', i1)
cv2.waitKey(0)
And the answer is that TurboJPEG is 7x faster! That is 4.6ms versus 32.2ms.
In [18]: %timeit i0 = cv2.imdecode(inp, cv2.IMREAD_COLOR)
32.2 ms ± 346 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [19]: %timeit i1 = jpeg.decode(r)
4.63 ms ± 55.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Kudos to @Nuzhny for spotting it first!
Updated Answer
I have been doing some further benchmarks on this and was unable to verify your claim that it is faster to save an image to disk and read it with imread() than it is to use imdecode() from memory. Here is how I tested in IPython:
import cv2
# First use 'imread()'
%timeit i1 = cv2.imread('image.jpg', cv2.IMREAD_COLOR)
116 ms ± 2.86 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
# Now prepare the exact same image in memory
r = open('image.jpg','rb').read()
inp = np.asarray(bytearray(r), dtype=np.uint8)
# And try again with 'imdecode()'
%timeit i0 = cv2.imdecode(inp, cv2.IMREAD_COLOR)
113 ms ± 1.17 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
So, I find imdecode() around 3% faster than imread() on my machine. Even if I include the np.asarray() into the timing, it is still quicker from memory than disk - and I have seriously fast 3GB/s NVME disks on my machine...
Original Answer
I haven't tested this but it seems to me that you are doing this in a loop:
read 1k bytes
append it to a buffer
look for JPEG SOI marker (0xffdb)
look for JPEG EOI marker (0xffd9)
if you have found both the start and the end of a JPEG frame, decode it
1) Now, most JPEG images with any interesting content I have seen are between 30kB to 300kB so you are going to do 30-300 append operations on a buffer. I don't know much abut Python but I guess that may cause a re-allocation of memory, which I guess may be slow.
2) Next you are going to look for the SOI marker in the first 1kB, then again in the first 2kB, then again in the first 3kB, then again in the first 4kB - even if you have already found it!
3) Likewise, you are going to look for the EOI marker in the first 1kB, the first 2kB...
So, I would suggest you try:
1) allocating a bigger buffer at the start and acquiring directly into it at the appropriate offset
2) not searching for the SOI marker if you have already found it - e.g. set it to -1 at the start of each frame and only try and find it if it is still -1
3) only look for the EOI marker in the new data on each iteration, not in all the data you have already searched on previous iterations
4) furthermore, actually, don't bother looking for the EOI marker unless you have already found the SOI marker, because the end of a frame without the corresponding start is no use to you anyway - it is incomplete.
I may be wrong in my assumptions, (I have been before!) but at least if they are public someone cleverer than me can check them!!!
I recommend to use turbo-jpeg. It has a python API: PyTurboJPEG.
@Andy Rosenblum's works, and it might be the best solution if using the outdated cv python API (vs. cv2).
However, because this question is equally interesting for users of the latest versions, I suggest the following solution. The sample code below may be better than the accepted solution because:
- It is compatible with newer OpenCV python API (cv2 vs. cv). This solution is tested under opencv 3.0 and python 3.0. I believe only trivial modifications would be required for opencv 2.x and/or python 2.7x.
- Fewer imports. This can all be done with numpy and opencv directly, no need for StringIO and PIL.
Here is how I create an opencv image decoded directly from a file object, or from a byte buffer read from a file object.
import cv2
import numpy as np
#read the data from the file
with open(somefile, 'rb') as infile:
buf = infile.read()
#use numpy to construct an array from the bytes
x = np.fromstring(buf, dtype='uint8')
#decode the array into an image
img = cv2.imdecode(x, cv2.IMREAD_UNCHANGED)
#show it
cv2.imshow("some window", img)
cv2.waitKey(0)
Note that in opencv 3.0, the naming convention for the various constants/flags changed, so if using opencv 2.x, you will need to change the flag cv2.IMREAD_UNCHANGED. This code sample also assumes you are loading in a standard 8-bit image, but if not, you can play with the dtype='...' flag in np.fromstring.
another way,
also in the case of a reading an actual file this will work for a unicode path (tested on windows)with open(image_full_path, 'rb') as img_stream:
file_bytes = numpy.asarray(bytearray(img_stream.read()), dtype=numpy.uint8)
img_data_ndarray = cv2.imdecode(file_bytes, cv2.CV_LOAD_IMAGE_UNCHANGED)
img_data_cvmat = cv.fromarray(img_data_ndarray) # convert to old cvmat if needed
If you have an image img (which is a numpy array) you can convert it into string using:
>>> img_str = cv2.imencode('.jpg', img)[1].tostring()
>>> type(img_str)
'str'
Now you can easily store the image inside your database, and then recover it by using:
>>> nparr = np.fromstring(STRING_FROM_DATABASE, np.uint8)
>>> img = cv2.imdecode(nparr, cv2.CV_LOAD_IMAGE_COLOR)
where you need to replace STRING_FROM_DATABASE with the variable that contains the result of your query to the database containing the image.
It works in 2020 with numpy==1.19.4 and opencv==4.4.0:
import cv2
cam = cv2.VideoCapture(0)
# get image from web camera
ret, frame = cam.read()
# convert to jpeg and save in variable
image_bytes = cv2.imencode('.jpg', frame)[1].tobytes()
Hi, I'm new to opencv and I'm trying to decode a byte array
From one side I'm sending I need to send a message in bytes format, and I'm using this code:
image_bytes = cv2.imencode('.jpg', imageRGB)[1].tobytes()
And from the receiving side, I'm am receiving a message with the following type: <class 'str'>
with this content: /9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAIBAQEBAQIBAQECAgICAgQDAgICAgUEBAMEBgUGBgYFBgYGBwkIBgcJBwYGCAsICQoK ...
I tried the following: (x['other']['contentBytes'] is where the bytes are)
nparr = np.fromstring(x['other']['contentBytes'], np.uint8)
This returns a ( <class 'numpy.ndarray'> ) with the following shape: (40672,)
And when I try to
newFrame = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
I get a <class 'NoneType'> type.