@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.
Answer from svohara on Stack Overflow@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
cv2 imdecode after transporting an image via multipart form
python - Get back a numpy array from JPEG encoded (by cv2.imencode) bytearray - Stack Overflow
python - How to encode a numpy array with PIL, like opencv's cv2.imencode()? - Stack Overflow
Python - byte image to NumPy array using OpenCV - Stack Overflow
I have my question posted on SO so I will drop the link instead of retyping everything. I did ask earlier about making the multipart encoded message and figured that out but I can't get the jpeg to build on the clients end.
https://stackoverflow.com/questions/69878024/cv2-imdecode-imencode-and-transporting-images-over-http-using-multipart-encoder
tl;dr is I send a request for object detection to a flask app, it replies with multipart encoded response with JSON about detection and an image converted from cv2.decode (numpy.ndarray) using .tobytes() for HTTP transport. The client receives the multipart, decodes the JSON but cant decode the image into a jpeg.
Here are some logs for the flow of what I am dealing with.
# Grabbing image using an http request and converting into a jpeg 11/07/21 20:44:30.623202 zm_mlapi[37535] DBG1 Media:659 ['std.out' --> image from ZM API as response.content - type(img) = <class 'bytes'> - len(img) = 205125] 11/07/21 20:44:30.627857 zm_mlapi[37535] DBG1 Media:661 ['std.out' --> after np.asarray(bytearray(img), np.uint8) - type(img) = <class 'numpy.ndarray'> - len(img) = 205125] 11/07/21 20:44:30.658582 zm_mlapi[37535] DBG1 Media:663 ['std.out' --> after cv2.imdecode(img, cv2.IMREAD_COLOR) - type(img) = <class 'numpy.ndarray'> - len(img) = 1080] 11/07/21 20:44:30.67595 zm_mlapi[37535] DBG2 pyzm_utils:386 [resize:img: success using resize=800.0 - original dimensions: 1920*1080 - resized dimensions: 450*800] 11/07/21 20:44:30.678568 zm_mlapi[37535] DBG1 Media:681 ['std.out' --> after resize - type(img) = <class 'numpy.ndarray'> - len(img) = 450] # returned image to the class that requested it (ML Pipeline) 11/07/21 20:44:30.687835 zm_mlapi[37535] DBG1 detect_sequence:1048 ['std.out' --> DETECT STREAM: FRAME RETURNED FROM MEDIA CLASS --> type(frame) = <class 'numpy.ndarray'> - len(frame) = 450] 11/07/21 20:44:33.582062 zm_mlapi[37535] DBG1 detect_sequence:1656 ['std.out' --> before returning matched data - type(matched_data['image']) = <class 'numpy.ndarray'> - len(matched_data['image']) = 450] # Return image to the flask app, now the flask app has to construct a response with JSON and the image 11/07/21 20:44:33.588139 zm_mlapi[37535] DBG1 mlapi:587 ['std.out' --> type(matched_data['image']) = <class 'numpy.ndarray'> - len(matched_data['image']) = 450] 11/07/21 20:44:33.591981 zm_mlapi[37535] DBG1 mlapi:590 ['std.out' --> before converting using .tobytes() - type(img) = <class 'numpy.ndarray'> - len(img) = 450] 11/07/21 20:44:33.596642 zm_mlapi[37535] DBG1 mlapi:594 ['std.out' --> after converting using .tobytes() - type(img) = <class 'bytes'> - len(img) = 1080000] 11/07/21 20:44:33.611218 zm_mlapi[37535] DBG1 mlapi:611 ['std.out' --> multipart MIME TYPE -> multipart/form-data; boundary=e7f7b825a51d4184ad7f12e7bbc6f411] # flask app returns the response to the client 11/07/21 21:00:58.393864 zmesdetect_m4[102768] DBG1 zm_detect:418 ['std.out' --> got json data] 11/07/21 21:00:58.395459 zmesdetect_m4[102768] DBG1 zm_detect:414 ['std.out' --> got an image with Content-Type - b'application/octet'] 11/07/21 21:00:58.396815 zmesdetect_m4[102768] DBG1 zm_detect:422 ['std.out' --> success = True] 11/07/21 21:00:58.398169 zmesdetect_m4[102768] DBG1 zm_detect:423 ['std.out' --> img - type(img) = <class 'bytes'> - len(img) = 1080000] 11/07/21 21:00:58.39958 zmesdetect_m4[102768] DBG1 zm_detect:424 ['std.out' --> img[:50] = b'\\gu\\gu\\gu]hv^iw_jx`kyalzgr\x80kv\x84it\x82it\x82it\x82it\x82it\x82it\x82ju'] 11/07/21 21:00:58.401012 zmesdetect_m4[102768] DBG1 zm_detect:426 ['std.out' --> img after np.frombuffer(img, dtype=np.uint8) -> type(np_img) = <class 'numpy.ndarray'>] 11/07/21 21:00:58.402911 zmesdetect_m4[102768] DBG1 zm_detect:430 ['std.out' --> img after np_img.copy() -> type(np_img) = <class 'numpy.ndarray'>] 11/07/21 21:00:58.404296 zmesdetect_m4[102768] DBG1 zm_detect:432 ['std.out' --> len(np_img)=1080000] 11/07/21 21:00:58.405619 zmesdetect_m4[102768] DBG1 zm_detect:433 ['std.out' --> attempting to decode numpy array into a jpeg] 11/07/21 21:00:58.407144 zmesdetect_m4[102768] DBG1 zm_detect:442 ['std.out' --> img after cv2.imdecode -> type(new_img) = <class 'NoneType'>] 11/07/21 21:00:58.408474 zmesdetect_m4[102768] DBG1 zm_detect:448 ['std.out' --> exiting due to image error]
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
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()