How to create an image from a string? - python

I'm trying to create an image file from a string, but so far I have not had any luck. Using ImageDraw I can print a string on an image file, but this is not what I intend to do. I want to generate just a simple image file from a set of characters.
Edit: Imagine a hypothetical case of creating an ASCII art text of a pic, but instead of creating an output of a text file, generate an image file. My code consists of multiple loops and using Matplotlib will affect my code's performance.

you could try matplotlib:
import matplotlib.pyplot as plt
plt.text(0,0,mystring)
plt.savefig("mysentence.png", dpi=100)

You can use the PIL.Image.frombytes method
import PIL
my_string = b'\xf8\xff\xb0\xbc\xd8]\xba\xdf0\xbd\xdeE\xfb\xff\xd1\xf1\xff\xbf\xb4\xd9p\xad\xd9F\xae\xd7U\xf2\xff\xd6\xdf\xff\xdc\xde\xff\xd2m\xa8L\xe0\xff\xc5\xe0\xff\xe1F\x9a\\I\x9e]E\x9bTD\x9aSK\x9fa1\xadO,\xa7L3\xaeT/\xa9R.\xa8Q'
img = Image.frombytes("RGB", (5,5), my_string)
img.show()
Note, that the length of your string must equals the number of bytes used for a pixel multiplied by the width and height of the image.
In my case my_string holds an RGB image, that makes 3 bytes per pixel and the image is 5 pixels width and 5 pixel high.
Therefore my_string should have a length of 75 bytes (3 * 5 * 5)

Related

Convert rgb to black and white in python

I want to use Python to convert a color image into a black and white image, but I should not use the library. But opening a file with a library is allowed, but converting it to black and white should not be done with a library. I know the CV2 library does this, but I want it to do without the library.
Assuming the image can be loaded as list in with a size of [x,y,3], where the last dimension contains the 3 color bytes as separate elements.
from PIL import Image
import numpy as np
raw_img = Image.open("your_image_here")
img = raw_img.load()
x,y = raw_img.size
threshold = 300
bw_img = [[0]*y]*x # blank image
for i in range(x):
for j in range(y):
if img[i,j] < threshold:
bw_img[i][j] = 0
else:
bw_img[i][j] = 1
Image.fromarray(np.asarray(bw_img),mode=1).save("your_nwe_image.bmp")
An image can be treated as 2D array/list with and additional dimension for the color values of each channel RGB.
This checks for every pixel whether the sum of the color values is above a threshold, and assigns the pixel as white. How you want to determine if a pixel will be white exactly or not is up to you.
But honestly doing this without a library is not really useful, even numpy is much faster and efficient for such a task.

Simplest way to convert a low-resolution black and white picture to a matrix

I have a set of very low-resolution pictures (in .png but I can easily convert them to something else). They all only have black or white pixels, like QR codes.
What I want is to be able to read them as binary matrix (a 1 for a black pixel and a zero for a white one).
I don't need anything more fancy than that, what should I use?
Hi you can use PIL to read the image, and then numpy to convert it to a matrix
from PIL import Image
import numpy as np
im = Image.read("imageName.ext")
im_mat = np.asarray(im)
Alternatively you can do all in one step with opencv
import cv2
img = cv2.imread("imageName.ext")
in both cases you will have a matrix with size WxHxC with H the height in pixels, W the widht and c the number of channels (3 or 4 depending if there's an alpha for transparency).
If your image is black and white and you only want a matrix with size WxH take one channel with
img = img_mat[:,:,0] #8-bit matrix
and last you can binarize that givving an umbral or just by comparing
bin = img> 128
or
bin = img == 255
I corrected this last line I had a typo in it

Convert gray pixel_array from DICOM to RGB image

I'm reading DICOM gray image file as
gray = dicom.dcmread(file).pixel_array
There I've got (x,y) shape but I need RGB (x,y,3) shape
I'm trying to convert using CV
img = cv2.cvtColor(gray, cv2.COLOR_GRAY2RGB)
And for testing I'm writing it to file cv2.imwrite('dcm.png', img)
I've got extremely dark image on output which is wrong, what is correct way to convert pydicom image to RGB?
To answer your question, you need to provide a bit more info, and be a bit clearer.
First what are you trying to do? Are you trying to only get an (x,y,3) array in memory? or are you trying to convert the dicom file to a .png file? ...they are very different things.
Secondly, what modality is your dicom image?
It's likely (unless its ultrasound or perhaps nuc med) a 16 bit greyscale image, meaning the data is 16 bit, meaning your gray array above is 16 bit data.
So the first thing to understand is window levelling and how to display a 16-bit image in 8 bits. have a look here: http://www.upstate.edu/radiology/education/rsna/intro/display.php.
If it's a 16-bit image, if you want to view it as a greyscale image in rgb format, then you need to know what window level you're using or need, and adjust appropriately before saving.
Thirdly, like lenik mention above, you need to apply the dicom slope/intercept values to your pixel data prior to using.
If your problem is just making a new array with extra dimension for rgb (so sizes (r,c) to (r,c,3)), then it's easy
# orig is your read in dcmread 2D array:
r, c = orig.shape
new = np.empty((w, h, 3), dtype=orig.dtype)
new[:,:,2] = new[:,:,1] = new[:,:,0] = orig
# or with broadcasting
new[:,:,:] = orig[:,:, np.newaxis]
That will give you the 3rd dimension. BUT the values will still all be 16-bit, not 8 bit as needed if you want it to be RGB. (Assuming your image you read with dcmread is CT, MR or equivalent 16-bit dicom - then the dtype is likely uint16).
If you want it to be RGB, then you need to convert the values to 8-bit from 16-bit. For that you'll need to decide on a window/level and apply it to select the 8-bit values from the full 16-bit data range.
Likely your problem above - I've got extremely dark image on output which is wrong - is actually correct, but it's dark because the window/level cv is using by default makes it 'look' dark, or it's correct but you didn't apply the slope/intercept.
If what you want to do is convert the dicom to png (or jpg), then you should probably use PIL or matplotlib rather than cv. Both of those offer easy ways to save a 16 bit 2D array (which is what you 'gray' is in your code above), both which allow you to specify window and level when saving to png or jpg. CV is complete overkill (meaning much bigger/slower to load, and much higher learning curve).
Some psueudo code using matplotlib. The vmin/vmax values you need to adjust - the ones here would be approximately ok for a CT image.
import matplotlib.pyplot as plt
df = dcmread(file)
slope = float(df.RescaleSlope)
intercept = float(df.RescaleIntercept)
df_data = intercept + df.pixel_array * slope
# tell matplotlib to 'plot' the image, with 'gray' colormap and set the
# min/max values (ie 'black' and 'white') to correspond to
# values of -100 and 300 in your array
plt.imshow(df_data, cmap='gray', vmin=-100, vmax=300)
# save as a png file
plt.savefig('png-copy.png')
that will save a png version, but with axes drawn as well. To save as just an image, without axes and no whitespace, use this:
inches = (3,3)
dpi = 150
fig, ax = plt.subplots(figsize=inches, dpi=dpi)
fig.subplots_adjust(left=0, right=1, top=1, bottom=0, wspace=0, hspace=0)
ax.imshow(df_data, cmap='gray', vmin=-100, vmax=300)
fig.save('copy-without-whitespace.png')
The full tutorial on reading DICOM files is here: https://www.kaggle.com/gzuidhof/full-preprocessing-tutorial
Basically, you have to extract parameters slope and interception from the DICOM file and do the math for every pixel: hu = pixel_value * slope + intercept -- all this explained in the tutorial with the code samples and pictures.

How to crop bottom part of an image(part with subtitle) in python

I am doing subtitle extraction from videos in python.I have used opencv in python to do this.I have divided it into frames and for each frame as image which will be stored in my disk, i am doing ocr on it.But I dont want to perform ocr on the entire image.I just want the subtitle part.I manually cropped the image with these values 278:360 as my image size was 360:640.But the image size varies for different video files.Now my question is how to crop the subtitle part alone programatically.Please do answer.Thanks in advance
textImage = image[278:360,:]
You can take the last third of the image height, if you are sure that the subtitles will be there.
For instance, for the following image:
Proceed as follows:
read the image into a numpy array :
In my example, I am using imread from skimage.io, but you can use opencv:
from skimage.io import imread
img = imread('http://cdn.wccftech.com/wp-content/uploads/2017/05/subtitle-of-a-blu-ray-movie.jpg')
img.shape # >>> (383, 703, 3)
Get the bottom third of the image (which contains the subtitle):
The idea is to divide the height of the image by 3 and take the bottom third of the image:
crop_position = int(img.shape[0]/3)
subtitle_img = img[img.[0] - crop_position:,:,:]
The resulting subtitle_img looks like this:
In my case I use only one library and regular operations on arrays:
import matplotlib.image as mpimg
image= mpimg.imread('someImage.jpg')
#Example for bottom half of an image, but you can replace this with your parameter
crop_position = image.shape[0] // 2
half_imagage = image[image.shape[0] - crop_position:,:]
And it returns a nice image:

Create set of random JPGs

Here's the scenario, I want to create a set of random, small jpg's - anywhere between 50 bytes and 8k in size - the actual visual content of the jpeg is irrelevant as long as they're valid. I need to generate a thousand or so, and they all have to be unique - even if they're only different by a single pixel. Can I just write a jpeg header/footer and some random bytes in there? I'm not able to use existing photos or sets of photos from the web.
The second issue is that the set of images has to be different for each run of the program.
I'd prefer to do this in python, as the wrapping scripts are in Python.
I've looked for python code to generate jpg's from scratch, and didn't find anything, so pointers to libraries are just as good.
If the images can be only random noise, so you could generate an array using numpy.random and save them using PIL's Image.save.
This example might be expanded, including ways to avoid a (very unlikely) repetition of patterns:
import numpy
from PIL import Image
for n in range(10):
a = numpy.random.rand(30,30,3) * 255
im_out = Image.fromarray(a.astype('uint8')).convert('RGB')
im_out.save('out%000d.jpg' % n)
These conditions must be met in order to get jpeg images:
The array needs to be shaped (m, n, 3) - three colors, R G and B;
Each element (each color of each pixel) has to be a byte integer (uint, or unsigned integer with 8 bits), ranging from 0 to 255.
Additionaly, some other way besides pure randomness might be used in order to generate the images in case you don't want pure noise.
If you do not care about the content of a file, you can create valid JPEG using Pillow (PIL.Image.new [0]) this way:
from PIL import Image
width = height = 128
valid_solid_color_jpeg = Image.new(mode='RGB', size=(width, height), color='red')
valid_solid_color_jpeg.save('red_image.jpg')
[0] https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.new
// EDIT: I thought OP wants to generate valid images and does not care about their content (that's why I suggested solid-color images). Here's a function that generates valid images with random pixels and as a bonus writes random string to the generated image. The only dependency is Pillow, everything else is pure Python.
import random
import uuid
from PIL import Image, ImageDraw
def generate_random_image(width=128, height=128):
rand_pixels = [random.randint(0, 255) for _ in range(width * height * 3)]
rand_pixels_as_bytes = bytes(rand_pixels)
text_and_filename = str(uuid.uuid4())
random_image = Image.frombytes('RGB', (width, height), rand_pixels_as_bytes)
draw_image = ImageDraw.Draw(random_image)
draw_image.text(xy=(0, 0), text=text_and_filename, fill=(255, 255, 255))
random_image.save("{file_name}.jpg".format(file_name=text_and_filename))
# Generate 42 random images:
for _ in range(42):
generate_random_image()
If you are looking for a way to do this without numpy this worked for me
(python 3.6 for bytes, you still need Pillow)
import random as r
from PIL import Image
dat = bytes([r.randint(1,3) for x in range(4500000)])
i = Image.frombytes('1', (200,200), dat)

Categories

Resources