61

I'm trying to load image from string like as PHP function imagecreatefromstring

How can I do that?

I have MySQL blob field image. I'm using MySQLdb and don't want create temporary file for working with images in PyOpenCV.

NOTE: need cv (not cv2) wrapper function

xercool
  • 883
  • 1
  • 8
  • 24

5 Answers5

139

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')
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.

ZdaR
  • 22,343
  • 7
  • 66
  • 87
jabaldonedo
  • 25,822
  • 8
  • 77
  • 77
  • working with cv2 and cv (import cv2, from cv2 import cv). but returned not lplimage and not cvMat. Function GetSize not working – xercool Jun 18 '13 at 14:51
  • `src=image_create_from_string(str) cv.GetSize(src)` – xercool Jun 18 '13 at 14:55
  • I suggest you to work only with cv2 as long as it uses numpy arrays which are much more efficient in Python than cvMat and lplimage. Function GetSize doesn't work in cv2 because cv2 uses numpy and you use np.shape(image) to get the size of your image. In any case, if you want to convert form cv2 ndarray to cv lplimage check this answer: http://stackoverflow.com/questions/11528009/opencv-converting-from-numpy-to-iplimage-in-python – jabaldonedo Jun 18 '13 at 14:55
  • I understand. Can you just write that function in cv wrapper? – xercool Jun 18 '13 at 15:01
  • I have updated the code so now you have the ouput in the CV format – jabaldonedo Jun 18 '13 at 15:23
  • 26
    With OpenCV 3+ use `cv2.IMREAD_COLOR` instead of `cv2.CV_LOAD_IMAGE_COLOR` – uglide Feb 04 '16 at 09:26
  • 4
    np.fromstring is deprecated now, we should use np.frombuffer. Secondly, if we use raw string in np.frombuffer, it would not provide appropriate numpy arrays. Therefore, it would better to use base64 library for better reading of the string something link `base64.b64decode(string)`. Lastly, I think this answer here (https://stackoverflow.com/a/58406222/1522905) is better match for this question. – Anugraha Sinha Nov 20 '19 at 15:48
  • cv2.__version__ Out[331]: '4.5.1' AttributeError: module 'cv2.cv2' has no attribute 'CV_LOAD_IMAGE_COLOR' – CS QGB Jan 27 '22 at 08:12
  • With Python 3.8 use `fd = open('foo.jpg','rb')` instead of `fd = open('foo.jpg')`. – smihael Feb 18 '22 at 22:07
29

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)
Anugraha Sinha
  • 621
  • 6
  • 13
10

I've try to use this code to create an opencv from a string containing a raw buffer (plain pixel data) and it doesn't work in that peculiar case.

So here's how to do that for this kind of data:

image = np.fromstring(im_str, np.uint8).reshape( h, w, nb_planes )

(but yes you need to know your image properties)

if your B and G channel is permuted, here's how to fix it:

image = cv2.cvtColor(image, cv2.cv.CV_BGR2RGB)
Alexandre Mazel
  • 2,462
  • 20
  • 26
8

I was following the solution from @jabaldonedo but it seems it's a bit old and need some adjustments.

I am using OpenCV 3.4.8.29 by the way.

im_path = 'path/to/foo.jpg'
with open(im_path, 'rb') as fp:
    im_b = fp.read()
image_np = np.frombuffer(im_b, np.uint8)
img_np = cv2.imdecode(image_np, cv2.IMREAD_COLOR)  

im_cv = cv2.imread(im_path)

print('Same image: {}'.format(np.all(im_cv == img_np)))

Same image: True

Eypros
  • 5,370
  • 6
  • 42
  • 75
1

One gotcha of imdecode:

If the buffer is too short or contains invalid data, the function returns [None]

This feels uncharacteristically lenient from OpenCV. Here's a function that accommodates for this:

import numpy as np
import cv2 as cv

def read_image(content: bytes) -> np.ndarray:
    """
    Image bytes to OpenCV image

    :param content: Image bytes
    :returns OpenCV image
    :raises TypeError: If content is not bytes
    :raises ValueError: If content does not represent an image
    """
    if not isinstance(content, bytes):
        raise TypeError(f"Expected 'content' to be bytes, received: {type(content)}")
    image = cv.imdecode(np.frombuffer(content, dtype=np.uint8), cv.IMREAD_COLOR)
    if image is None:
        raise ValueError(f"Expected 'content' to be image bytes")
    return image
kym
  • 818
  • 7
  • 12