You can convert to a string like this:
import base64
with open("image.png", "rb") as image:
b64string = base64.b64encode(image.read())
That should give you the same results as if you run this in Terminal:
base64 < image.png
And you can convert that string back to a PIL Image like this:
from PIL import Image
import io
f = io.BytesIO(base64.b64decode(b64string))
pilimage = Image.open(f)
That should be equivalent to the following in Terminal:
base64 -D < "STRING" > recoveredimage.png
Note that if you are sending this over LoRa, you are better off sending the PNG-encoded version of the file like I am here as it is compressed and will take less time. You could, alternatively, send the expanded out in-memory version of the file but that would be nearly 50% larger. The PNG file is 13kB. The expanded out in-memory version will be 100*60*3, or 18kB.
Answer from Mark Setchell on Stack OverflowHow do you convert a png image to a string, and then convert the string to a PIL (python image object)?
The use case is I have a python script that gets a string from a server api. The string is an image that is encoded as a string. The script needs to take that string from the server and reconvert it back to an image file that I can use opencv with etc. I don't want to save the image file to disk, I just want to work with it in the memory.
This is what I have for now below. So the first part of the code is going to be run on my server, and will convert 1.png into a base64 encoded string.
The second part of the code should ideally convert that base64 encoded string back into an image file that resides in the memory (Do NOT want to save onto the disk). The issue is I'm having trouble converting it back to an workable image type (for use with PIL, openCV, etc) loaded in memory. When I print image2, the output does not equal when I print image1.
What am I doing wrong? How do I fix this?
import io
import requests
from PIL import Image
import base64
with open('1.png', "rb") as imageFile:
image1 = base64.b64encode(imageFile.read())
print(image1)
image2 = base64.b64decode(image1)
print(image2)Iโm trying to transmit an image and my radio accepts text strings. I want to be able to automatically take an image file and turn it into text. Iโm kind of stuck.
Assuming you have the image represented as a numpy array (since the question is tagged as OpenCV related, then this is likely the case), then to obtain the result you want, I'd take the following steps.
- First
flattenthe array to make it linear. - Then turn it into a regular python list using
tolist - Convert all the elements into strings using
mapandstr - Join all the elements using spaces.
In steps it would look something like
# img is our input image represented by a numpy array
lin_img = img.flatten()
pixel_list = lin_img.tolist()
pixel_str_list = map(str, pixel_list)
img_str = ' '.join(pixel_str_list)
or, put together
# img is our input image represented by a numpy array
img_str = ' '.join(map(str,img.flatten().tolist()))
Let's call this Variant 2 for performance testing purposes.
Update 1
Since numpy arrays are themselves iterable, we can skip the second step.
# img is our input image represented by a numpy array
img_str = ' '.join(map(str,img.flatten()))
Unfortunately it seems that skipping this step has fairly significant negative effect on performance.
Let's call this Variant 3 for performance testing purposes.
Update 2
User Manel Fornos (deleted) answer gave me another idea. Although this approach is a bit hackish, it is somewhat faster.
The gist is to use the existing facilities to get a string represenation of a list, and filter out unwanted characters.
str_rep = str(img.flatten().tolist())
img_str = str_rep.strip('[]').replace(',','')
Let's call this Variant 4 for performance testing purposes.
Variant 1 will be a fixed up version of Liam Lawrence's code:
pxList = ''
# The height and width of your Mat
height = np.size(img, 0)
width = np.size(img, 1)
# Iterates through the values of your Mat and stores them in pxList
for i in range(height):
for j in range(width):
pxList = pxList + " " + str(img[i][j])
pxList = pxList[1:] # Drop the first space
I wrote a simple little script to compare the algorithms (the full code is on pastebin). Here are the results:
# Pixels, Variant 1 (ms), Variant 2 (ms), Variant 3 (ms), Variant 4 (ms)
(1024, 2.8326225819203277, 0.13493335046772717, 1.5932890912113131, 0.09023493209332506)
(4096, 13.339841376487794, 0.5257651461289086, 6.325210327010836, 0.3265428986086241)
(9216, 32.98282323591406, 1.1823080866422975, 14.354809759340927, 0.7088365979475153)
(16384, 75.67087786296861, 2.1013669335069043, 26.917736751458644, 1.2577715882884644)
(25600, 137.34306664673863, 3.3527305844737176, 39.52922089259947, 1.9327700867009523)
(36864, 253.29441311675095, 4.734033934480575, 59.513813906516, 2.9113162427067962)
(50176, 451.560393848939, 6.5756611524649955, 80.0690276278131, 3.998343364868928)
(65536, 730.1453117644841, 8.744634443763166, 103.20875278841335, 5.7598277155337385)
(82944, 1111.2658522242352, 11.029055368769303, 131.75812149309473, 7.009532636131244)
(102400, 1660.044328259597, 13.671936656754369, 163.50234457172607, 8.832774137495392)
(123904, 3752.484254283715, 16.593065599119328, 196.8919234148476, 10.672515640955282)
(147456, 6808.498583618867, 20.05951524565397, 238.21070485215222, 13.339090582743296)
(173056, 11572.846199726502, 23.518125208653373, 275.5151841924039, 15.51396546209105)
(200704, 17107.24135330049, 27.29446060882168, 319.9635533287051, 17.9888784747817)
(230400, 24915.183616213795, 31.83344531218779, 368.9712484407863, 21.44858843792008)
(262144, 34914.46058437594, 36.754758635524354, 423.5016077462319, 24.536341210961155)


Update 3
Looking at the timings, one striking issue with Variant 1 is that its performance doesn't scale linearly with the size of input (number of pixels), as one may expect. Instead it looks more like O(n^2). The obvious culprit is the string addition -- since strings in Python are immutable, we keep copying progressively longer and longer strings as we add each pixel value.
One possible way to mitigate this problem is to use the cStringIO module.
output = cStringIO.StringIO()
# The height and width of your Mat
height = np.size(img, 0)
width = np.size(img, 1)
# Iterates through the values of your Mat and stores them in pxList
for i in range(height):
for j in range(width):
output.write(str(img[i][j]) + " ")
output.truncate(output.tell() - 1)
img_str = output.getvalue()
Let's call this Variant 5 for performance testing purposes.
Let's also include Manel Fornos' options, comprehension lists (Variant 6) and generators (Variant 7) for completeness.
# Number of pixels, variants 1..7 (ms)
1024, 2.7356, 0.1330, 1.5844, 0.0870, 2.5578, 1.7027, 1.7354
4096, 13.0483, 0.5250, 6.3810, 0.3227, 10.3566, 6.7979, 6.9346
9216, 34.9096, 1.1787, 14.2764, 0.7047, 23.0620, 15.1704, 15.3179
16384, 72.0128, 2.1126, 25.5553, 1.2306, 41.0506, 27.7385, 28.6510
25600, 142.5863, 3.2655, 40.1804, 1.9044, 64.5345, 42.0542, 42.7847
36864, 265.1944, 4.7110, 57.3741, 2.9238, 94.8722, 62.3143, 61.8108
50176, 444.3202, 6.6906, 78.9869, 4.1656, 126.9877, 82.6736, 84.2270
65536, 739.3482, 8.6936, 101.6483, 5.5619, 163.1796, 110.7537, 111.7517
82944, 1125.0065, 11.1771, 133.8886, 7.0509, 209.9322, 137.3384, 143.7916
102400, 1700.3401, 13.8166, 161.2337, 8.7119, 261.8374, 171.3757, 175.0435
123904, 2304.6573, 16.8627, 196.3455, 10.8982, 314.8287, 205.1966, 210.4597
147456, 5595.0777, 19.8212, 240.1495, 12.9097, 381.7084, 251.7319, 253.3573
173056, 10813.7815, 23.5161, 273.9376, 15.6852, 441.5994, 291.8913, 295.0038
200704, 17561.0637, 27.4871, 322.6305, 17.9567, 517.7028, 340.2233, 342.6525
230400, 25331.5150, 31.6211, 368.3908, 21.0858, 597.7710, 387.3542, 398.9715
262144, 34097.1663, 36.3708, 420.1081, 23.9135, 677.7977, 443.1318, 453.0447


Use PIL and numpy
from PIL import Image
import numpy as np
img = Image.open('lena_bw.jpg')
print (np.array(img))
[[135 137 138 ..., 148 131 92]
[136 137 138 ..., 149 134 96]
[137 138 138 ..., 149 135 96]
...,
[ 20 21 24 ..., 71 71 70]
[ 21 22 26 ..., 68 70 73]
[ 23 24 28 ..., 67 69 75]]
The result is an array of image pixels, it's up to you convert to the strings.
I think you were close. Try:
pBits = im.convert("RGBA").tostring("raw", "RGBA")
The image first has to be converted to RGBA mode in order for the RGBA rawmode packer to be available (see Pack.c in libimaging). You can check that len(pBits) == im.size[0]*im.size[1]*4, which is 200x200x4 = 160,000 bytes for your gloves200 image.
Have you tried using the conversion inside the tostring function directly?
im = open("test.bmp")
imdata = im.tostring("raw", "RGBA", 0, -1)
w, h = im.size[0], im.size[1]
glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, imdata)
Alternatively use compatibility version:
try:
data = im.tostring("raw", "BGRA")
except SystemError:
# workaround for earlier versions
r, g, b, a = im.split()
im = Image.merge("RGBA", (b, g, r, a))