Mohamed Moustafa
Mohamed Moustafa

Reputation: 377

Writing text on a grayscale low resolution image

I have been trying to write text to an 80x80 16-bit grayscale image and have been having some trouble getting it to work.

I am currently using:

image = im[0]/255.0 #where im is just an np array of images (which are 80x80 np arrays)
  
# font
font = cv2.FONT_HERSHEY_SIMPLEX
  
# org
org = (40, 15)
 
# fontScale
fontScale = 0.3
   
# Blue color in BGR
color = (255.0)
  
# Line thickness of 2 px
thickness = 1
   
# Using cv2.putText() method
image = cv2.putText(image, 'Out:16', org, font, fontScale, color, thickness, cv2.LINE_AA)
   
# Displaying the image
cv2.imshow(window_name, image) 

However, not only does the text look pretty awe full and take up a lot of space (I cant go lower without it not being legible), the images becomes all black except of the text which is white.

enter image description here

Would there be a better way to write text to a low resolution image (make the text smaller)? And why is the image turned to all black?

EDIT:

I tried using ImageDraw() and the result is all greyed

from PIL import Image, ImageFont, ImageDraw 
      
# creating a image object 
image = Image.fromarray(im[0]/255.0)
  
draw = ImageDraw.Draw(image) 
  
# specified font size
font = ImageFont.truetype('./arial.ttf', 10) 
  
text = 'fyp:16'
  
# drawing text size
draw.text((5, 5), text, font = font, align ="left")

enter image description here

Upvotes: 1

Views: 2108

Answers (1)

Rotem
Rotem

Reputation: 32094

It looks like the main issue is converting image type to float.

Assume (please verify it):
im[0] is 16-bit grayscale, and im[0].dtype is dtype('uint16').

image = im[0]/255.0 implies that you want to convert the range from 16-bit grayscale to the the range of uint8.
Note: for converting the range from [0, 2^16-1] to [0, 255] you need to divide by (2**16-1)/255 = 257.0. But this is not the main issue.

The main issue is converting the type to float.
The valid range of float images in OpenCV is [0, 1].
All values above 1.0 are white pixels, and 0.5 is a gray pixel.


You can keep the image type uint16 - you don't have to convert it to uint8.
A white text color for uint16 type is 2**16-1 = 65535 (not 255).

Here is code sample that works with 16-bit grayscale (and uint16 type):

import numpy as np
import cv2

im = np.full((80, 80), 10000, np.uint16)  # 16 bits grayscale synthetic image - set all pixels to 10000
cv2.circle(im, (40, 40), 10, 0, 20, cv2.LINE_8) # draw black cicle - synthetic image

#image = im[0]/255.0 #where im is just an np array of images (which are 80x80 np arrays)
image = im #where im is just an np array of images (which are 80x80 np arrays)

color = 2**16-1  # 65535 is white color for 16 bis image

# Using cv2.putText() method
image = cv2.putText(image, 'Out:16', (40, 15), cv2.FONT_HERSHEY_SIMPLEX, 0.3, color, 1, cv2.LINE_AA)

# Displaying the image
cv2.imshow("image", image)
cv2.waitKey()

The above code creates synthetic 16-bit grayscale for testing.


Converting from 16-bit grayscale to 8-bit grayscale:

# https://stackoverflow.com/questions/11337499/how-to-convert-an-image-from-np-uint16-to-np-uint8
uint8_image = cv2.convertScaleAbs(image, alpha=255.0/(2**16-1))  # Convent uint16 image to uint8 image (2**16-1 scaled to 255)

The above conversion assumes image is full range 16 bits (pixel range [0, 65535]).


About the font:
OpenCV is computer vision oriented, and text drawing limited.

enter image description here


Why is the image black?

It's hard to answer without knowing the values of im[0].

  • It could be that im[0] is not a 16-bit grayscale at all.
  • It could be that the values of im[0] are very small.
  • It could be that the type of im[0] is not uint16.

Drawing text using Pillow (PIL):

The quality of the small text is much better compared to OpenCV.
You can find for about quality text rendering here.

Continue with the uint8 image:

pil_image = Image.fromarray(uint8_image)
  
draw = ImageDraw.Draw(pil_image)
  
# specified font size
font = ImageFont.truetype('./arial.ttf', 10) 
  
text = 'fyp:16'
  
# drawing text size
draw.text((5, 5), text, 255, font = font, align ="left")
pil_image.show()

Result:

enter image description here

I don't really know the reason your text looks wired compared to above result.

Upvotes: 1

Related Questions