51

I use python3 with numpy, scipy and opencv.

I'm trying to convert a image read through OpenCV and connected camera interface into a binary string, to send it within a json object through some network connection.

I have tried enconding the array as jpg and the decode the UTF-16 string, but I get no usable results. as example, with

img = get_image()
converted = cv2.imencode('.jpg', img)[1].tostring()
print(converted)

I get a byte-string as result:

b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00C\x00\x02\x01\x01\x01\x01\x01\x02\x01....

But this data cannot be used as content of a json object, because it contains invalid characters. Is there a way I can display the real bytes behind this string? I believe that \xff represents byte value FF, so I need as String like FFD8FFE0... and so on, instead of \xff\xd8\xff\xe0. What am I doing wrong?

I tried to encode it as UTF-8 and UTF16 after the code above, but I get several errors on that:

utf_string = converted.decode('utf-16-le')

UnicodeDecodeError: 'utf-16-le' codec can't decode bytes in position 0-1: illegal UTF-16 surrogate

text = strrrrrr.decode('utf-8')

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte

I can't figure out a way to get this right.

I also tried to convert it into a base64 encoded string, like explained in http://www.programcreek.com/2013/09/convert-image-to-string-in-python/ But that doesn't work either. ( This solution is not preferred, as it requires the image being written temporarly to disk, which is not exactly what I need. Preferrably the image should only be hold in memory, never on disk.)

The solution should contain a way to encode the image as json-conform string and also a way to decode it back to numpy-array, so it can be used again with cv2.imshow().

Thanks for any help.

Chr
  • 875
  • 1
  • 10
  • 27
Jack O'Neill
  • 1,032
  • 2
  • 19
  • 34
  • 2
    You can use base64 encoding ["in-memory"](http://stackoverflow.com/a/33522724/5008845) – Miki Dec 02 '16 at 09:07

2 Answers2

83

You do not need to save the buffer to a file. The following script captures an image from a webcam, encodes it as a JPG image, and then converts that data into a printable base64 encoding which can be used with your JSON:

import cv2
import base64

cap = cv2.VideoCapture(0)
retval, image = cap.read()
retval, buffer = cv2.imencode('.jpg', image)
jpg_as_text = base64.b64encode(buffer)
print(jpg_as_text)
cap.release()

Giving you something starting like:

/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAIBAQEBAQIBAQECAgICAgQDAgICAgUEBAMEBgUGBgYFBgYGBwkIBgcJBwYGCAsICQoKCg

This could be extended to show how to convert it back to binary and then write the data to a test file to show that the conversion was successful:

import cv2
import base64

cap = cv2.VideoCapture(0)
retval, image = cap.read()
cap.release()

# Convert captured image to JPG
retval, buffer = cv2.imencode('.jpg', image)

# Convert to base64 encoding and show start of data
jpg_as_text = base64.b64encode(buffer)
print(jpg_as_text[:80])

# Convert back to binary
jpg_original = base64.b64decode(jpg_as_text)

# Write to a file to show conversion worked
with open('test.jpg', 'wb') as f_output:
    f_output.write(jpg_original)

To get the image back as an image buffer (rather than JPG format) try:

jpg_as_np = np.frombuffer(jpg_original, dtype=np.uint8)
image_buffer = cv2.imdecode(jpg_as_np, flags=1)
Martin Evans
  • 45,791
  • 17
  • 81
  • 97
  • looks good, but how can I decode it back to an numpy array? the b64decode() returns decoded string. I tried image2 = cv2.imdecode(jpg_original, -1), but I get TypeError: buf is not a numpy array, neither a scalar. – Jack O'Neill Dec 02 '16 at 13:04
  • 7
    To get it back as an image buffer (rather than JPG format) try: `jpg_as_np = np.frombuffer(jpg_original, dtype=np.uint8); image_buffer = cv2.imdecode(jpg_as_np, flags=1)` – Martin Evans Dec 02 '16 at 13:19
  • yay, now it's working with this addition. thank you very much! – Jack O'Neill Dec 02 '16 at 13:29
  • Could not load image “test.jpg”. Error interpreting JPEG image file (Not a JPEG file: starts with 0x2f 0x39) I am getting this error when I open test.jpg – Himanshu Suthar Jun 03 '19 at 07:03
29

Some how the above answer doesn't work for me, it needs some update. Here is the new answer to this:

To encode for JSON:

import base64
import json
import cv2

img = cv2.imread('./0.jpg')
string = base64.b64encode(cv2.imencode('.jpg', img)[1]).decode()
dict = {
    'img': string
}
with open('./0.json', 'w') as outfile:
    json.dump(dict, outfile, ensure_ascii=False, indent=4)

To decode back to np.array:

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)

Hope this could help someone :P

lamhoangtung
  • 834
  • 2
  • 10
  • 22