31

I'm working on an app that to do some facial recognition from a webcam stream. I get base64 encoded data uri's of the canvas and want to use it to do something like this:

cv2.imshow('image',img)

The data URI looks something like this:

 data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7

So, for clarity I've shown what the image looks like so the base64 string is not broken.

<img src="data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7">

The official doc says, that imread accepts a file path as the argument. From this SO answer, if I do something like:

 import base64
 imgdata = base64.b64decode(imgstring) #I use imgdata as this variable itself in references below
 filename = 'some_image.jpg'
 with open(filename, 'wb') as f:
    f.write(imgdata)

The above code snippet works and the image file gets generated properly. However I don't think so many File IO operations are feasible considering I'd be doing this for every frame of the stream. I want to be able to read the image into the memory directly creating the img object.

I have tried two solutions that seem to be working for some people.

  1. Using PIL reference:

    pilImage = Image.open(StringIO(imgdata))
    npImage = np.array(pilImage)
    matImage = cv.fromarray(npImage)
    

    I get cv not defined as I have openCV3 installed which is available to me as cv2 module. I tried img = cv2.imdecode(npImage,0), this returns nothing.

  2. Getting the bytes from decoded string and converting it into an numpy array of sorts

    file_bytes = numpy.asarray(bytearray(imgdata), dtype=numpy.uint8)
    img = cv2.imdecode(file_bytes, 0) #Here as well I get returned nothing
    

The documentation doesn't really mention what the imdecode function returns. However, from the errors that I encountered, I guess it is expecting a numpy array or a scalar as the first argument. How do I get a handle on that image in memory so that I can do cv2.imshow('image',img) and all kinds of cool stuff thereafter.

I hope I was able to make myself clear.

Community
  • 1
  • 1
Vivek Pradhan
  • 4,777
  • 3
  • 26
  • 46

4 Answers4

36

This is my solution for python 3.7 and without using PIL

import base64

def readb64(uri):
   encoded_data = uri.split(',')[1]
   nparr = np.fromstring(base64.b64decode(encoded_data), np.uint8)
   img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
   return img

i hope that this solutions works for all

Javier Vallejos
  • 402
  • 1
  • 4
  • 2
34

(Edit: Updated for python 3) This worked for me on python 3, and doesn't require PIL/pillow or any other dependencies (except cv2):

import cv2
import numpy as np
import base64

def data_uri_to_cv2_img(uri):
    encoded_data = uri.split(',')[1]
    nparr = np.frombuffer(base64.b64decode(encoded_data), np.uint8)
    # old (python 2 version):
    # nparr = np.fromstring(encoded_data.decode('base64'), np.uint8)

    img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
    return img

data_uri = "data:image/jpeg;base64,/9j/4AAQ..."
img = data_uri_to_cv2_img(data_uri)
cv2.imshow("Image", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Lior
  • 1,169
  • 1
  • 9
  • 9
  • 2
    You saved my life ! I'm (obliged to) using Python 2 on AWS and the first solution kept giving me an error like this: https://stackoverflow.com/questions/28226308/cv2-cvtcolor-error-215-scn-3-scn-4-in-function-cvcvtcolor – Lewen May 29 '17 at 13:04
  • 7
    if returning type(img) i get NoneType. Seems the cv2.imdecode(nparr, cv2.IMREAD_COLOR) does not work as wanted – Joe Platano Oct 19 '17 at 21:39
  • @JoePlatano So what is the solution? – arqam Dec 11 '18 at 05:04
  • 1
    `np.fromstring` is deprecated on byte input, instead use `np.frombuffer`. – remarcoble Jun 14 '22 at 12:07
14

You can just use both cv2 and pillow like this:

import base64
from PIL import Image
import cv2
from StringIO import StringIO
import numpy as np

def readb64(base64_string):
    sbuf = StringIO()
    sbuf.write(base64.b64decode(base64_string))
    pimg = Image.open(sbuf)
    return cv2.cvtColor(np.array(pimg), cv2.COLOR_RGB2BGR)

cvimg = readb64('R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7')
cv2.imshow(cvimg)
kramer65
  • 50,427
  • 120
  • 308
  • 488
Jonno_FTW
  • 8,601
  • 7
  • 58
  • 90
  • 2
    Hi, I am running this piece of code but it is throwing: line 9, in readb64 sbuf.write(base64.b64decode(base64_string)) TypeError: string argument expected, got 'bytes' How to fix this? – Abhilash Ramteke Mar 24 '21 at 03:33
  • @AbhilashRamteke change `base64_string` to `base64_string.encode('ascii')` – Jonno_FTW Mar 25 '21 at 04:58
0

I found this simple solution.

import cv2
import numpy as np
import base64
image = ""  # raw data with base64 encoding
decoded_data = base64.b64decode(image)
np_data = np.fromstring(decoded_data,np.uint8)
img = cv2.imdecode(np_data,cv2.IMREAD_UNCHANGED)
cv2.imshow("test", img)
cv2.waitKey(0)

Source : https://gist.github.com/HoweChen/7cdd09b08147133d8e1fbe9b52c24768

Faizi
  • 410
  • 1
  • 8
  • 13