I'm trying to write a program in Python that takes an image and replaces the pixels with images, based off of the pixels lightness value. Figured I'd start with following a tutorial on converting images to ASCII art, then I can replace the ASCII characters with images. Here's what this attempt looks like:
https://pastebin.com/VNFWd9xN
It's a bit quick and dirty, just to see if I can make it work, but I think you'll get the idea.
So, a couple of issues I ran into.
The first and biggest one, i get a "TypeError: sequence item 0: expected str instance, JpegImageFile found". I get that the program expects a string and gets an image instead. But, how do I solve that?
Lastly, more of a parenthesis really, but I was playing around with the save function and could not get it so save "ascii_image" to jpg.
Anyhow, would really appreciate some guidance here.
Thank you.
import PIL.Image
from PIL import Image
img1 = Image.open("image1.jpg")
img2 = Image.open("image2.jpg")
img3 = Image.open("image3.jpg")
img4 = Image.open("image4.jpg")
img5 = Image.open("image5.jpg")
img6 = Image.open("image6.jpg")
img7 = Image.open("image7.jpg")
img8 = Image.open("image8.jpg")
img9 = Image.open("image9.jpg")
img10 = Image.open("image10.jpg")
img11 = Image.open("image11.jpg")
img12 = Image.open("image12.jpg")
# ascii characters used to build the output text
#ASCII_CHARS = ["#", "#", "S", "%", "?", "*", "+", ";", ":", ",", "."]
ASCII_CHARS = [img1, img2, img3, img4, img5, img6, img8, img9, img10, img11, img12]
# resize image according to a new width
def resize_image(image, new_width=80):
width, height = image.size
ratio = height/width
new_height = int(new_width * ratio)
resized_image = image.resize((new_width, new_height))
return(resized_image)
# convert each pixel to greyscale
def grayify(image):
grayscale_image = image.convert("L")
return(grayscale_image)
# convert pixels to a string of ASCII characters
def pixels_to_ascii(image):
pixels = image.getdata()
characters = "".join([ASCII_CHARS[pixel//25] for pixel in pixels])
return(characters)
def main(new_width=80):
# attempt to open image from user-input
path = input("Enter path to image:\n")
try:
image = PIL.Image.open(path)
except:
print(path, " is not a valid pathname to an image")
return
# convert image to ASCII
new_image_data = pixels_to_ascii(grayify(resize_image(image)))
# format
pixel_count = len(new_image_data)
ascii_image = "\n".join([new_image_data[index:(index+new_width)] for index in range(0, pixel_count, new_width)])
# print result
print(ascii_image)
# save
#with Image.open("ascii_image.jpg") as f:
# f.write(ascii_image)
main()
Related
I use opencv to count the number of white and black pixels of picture(I have convert them into black and white image), and everytime I run my code it return the number is 0,and the code is
output_path = "/content/drive/MyDrive/dataset_demo/result_pic"
for pic in os.listdir(output_path):
if pic.endswith('.jpg'):
image = cv2.imread(pic,cv2.IMREAD_UNCHANGED)
number_of_white_pix = np.sum(image == 255)
number_of_black_pix = np.sum(image == 0)
number_of_total = number_of_white_pix + number_of_black_pix
number_of_ratio = number_of_white_pix / number_of_black_pix
print(number_of_total)
The pic variable contains only the file name of the image, but cv2.imread needs the full path to the image in order to read it. You need to use the full path to the image when you call cv2.imread.
output_path = "/content/drive/MyDrive/dataset_demo/result_pic"
for pic in os.listdir(output_path):
if pic.endswith('.jpg'):
pic = os.path.join(output_path, pic) #full path to the image
image = cv2.imread(pic,cv2.IMREAD_UNCHANGED)
number_of_white_pix = np.sum(image == 255)
number_of_black_pix = np.sum(image == 0)
number_of_total = number_of_white_pix + number_of_black_pix
number_of_ratio = number_of_white_pix / number_of_black_pix
print(number_of_total)
I have the picture below used in Tesseract OCR:
My code to process the picture is:
# HOCR
with image[450:6200, 840:3550] as cropped:
imgPage = wi(image = cropped)
imageBlob = imgPage.make_blob('png')
horas = gerarHocr(imageBlob)
def gerarHocr(imageBlob):
image = Image.open(io.BytesIO(imageBlob))
markup = pytesseract.image_to_pdf_or_hocr(image, lang='por', extension='hocr', config='--psm 6')
soup = BeautifulSoup(markup, features='html.parser')
spans = soup.find_all('span', {'class' : 'ocrx_word'})
listHoras = []
...
return listHoras
Although my OCR is getting sometimes confused and duplicating 8 with 3 and returning 07:44/14:183 instead of 07:44/14:13 for example.
I think if I remove the grey lines using Wand I improve the confidence of the OCR.
How do I do that, please?
Thank you,
If the system is using ImageMagick-6, you can call Image.threshold(), but might need to remove the transparency first.
with Image(filename='PWILE.png') as img:
img.background_color = 'WHITE'
img.alpha_channel = False
img.threshold(threshold=0.5)
img.save(filename='output_threshold.png')
If you're using ImageMagick-7 (anything above version 7.0.8-41), then Image.auto_threshold() will work.
with Image(filename='support/PWILE.png') as img:
img.auto_threshold(method='otsu')
I would use cv2 and/or numpy.array
to convert light gray colors to white
img[ img > 128 ] = 255
to convert dark gray colors to black
img[ img < 128 ] = 0
import cv2
folder = '/home/user/images/'
# read it
img = cv2.imread(folder + 'old_img.png')
# convert ot grayscale
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# reduce colors
img[ img > 128 ] = 255
img[ img < 128 ] = 0
# save it
cv2.imwrite(folder + 'new_img.png', img)
# display result
#cv2.imshow('window', img)
#cv2.waitKey(0) # press any key in window to close it
#cv2.destroyAllWindows()
Result
Why does image.getdata() return different result second time I invoke it?
from PIL import Image
def write():
image = Image.open('image.jpg')
newimage = Image.new(image.mode, image.size)
pixels = [p for p in image.getdata()]
for i in range(100):
pixels[i] = (255,255,255)
newimage.putdata(pixels)
newimage.save('newimage.jpg')
print(list(newimage.getdata())[0:10])
def read():
image = Image.open('newimage.jpg')
pixels = [p for p in image.getdata()]
print(list(image.getdata())[0:10])
write()
read()
It gives me the following result:
Why is second set of data differ from the first one?
I've read another thread on SO regarding image rotation here:
PIL thumbnail is rotating my image?
and I've read another thread on SO about EXIF preservation here:
Preserve exif data of image with PIL when resize(create thumbnail)
Unfortunately, after implementing the suggestions above, it seems that I can only have either:
1) a saved rotated image without EXIF data
or
2) a non-rotated image with EXIF data
but it seems that I can't have both.
I'm hoping I can get some help to fix what I thought was a real simple problem, but has been turning into a scream-fest against my computer for the past few hours.
Here's the relevant parts of my code:
from PIL import Image, ExifTags
import piexif
currImage = Image.open(inFileName)
exif_dict = piexif.load(currImage.info["exif"])
for orientation in ExifTags.TAGS.keys():
if ExifTags.TAGS[orientation]=='Orientation':
break
exif=dict(currImage._getexif().items())
if exif[orientation] == 3:
currImage=currImage.rotate(180, expand=True)
elif exif[orientation] == 6:
currImage=currImage.rotate(270, expand=True)
elif exif[orientation] == 8:
currImage=currImage.rotate(90, expand=True)
currWidth, currHeight = currImage.size
# Here is where I can only do one or the other. Don't know enough about how to get both
exif_bytes = piexif.dump(exif_dict)
#exif_bytes = piexif.dump(exif)
maxImageDimension = [1280, 640, 360, 160]
for imgDim in maxImageDimension:
thumbRatio = imgDim / max(currWidth, currHeight)
# note that because Python's round function is mathematically incorrect, I have to do the following workaround
newWidth = int(Decimal(str(thumbRatio * currWidth)).quantize(Decimal('0.'), rounding=ROUND_UP))
newHeight = int(Decimal(str(thumbRatio * currHeight)).quantize(Decimal('0.'), rounding=ROUND_UP))
# copy currImage object
newImage = currImage
# note that I have to do resize method because thumbnail method has same rounding problem
newImage = newImage.resize((newWidth, newHeight))
# save the thumbnail
if imgDim == 1280:
outFileName = destinationDir + '\\' + file[:len(file)-4] + '.jpg'
else:
outFileName = destinationDir + '\\' + file[:len(file)-4] + '-' + str(newWidth) + 'x' + str(newHeight) + '.jpg'
print('Writing: ' + outFileName)
# Here is where I have to choose between exif or exif_bytes when saving but I can only get one or the other desired result
newImage.save(outFileName, exif=exif_bytes)
print('\n')
currImage.close()
newImage.close()
Thanks in advance.
I have a group of RGBA images saved in a folder, my goal is to convert these images into another folder in a pgm format, below is the code:
path1 = file/path/where/image/are/stored
path2 = file/path/where/pgm/images/will/be/saved
list = os.listdir(path1)
for file in listing:
#Transforms an RGBA with channel into an RGB only
image_rgb = Image.open(file).convert('RGB')
#Color separation stains to detect microscopic cells
ihc_hed = rgb2hed(image_rgb)
#Trasnforms the image into a numpy array of the UINT8 Type
cv_img = ihc_hed.astype(np.uint8)
# create color boundaries boundaries detecting black and blue stains
lower = np.array([0,0,0], dtype = "uint8")
upper = np.array([0,0,255], dtype = "uint8")
#calculates the pixel within the specified boundaries and create a mask
mask = cv2.inRange(cv_img, lower, upper)
img = Image.fromarray(mask,'L')
img.save(path2+file,'pgm')
however I get an error stating KeyError: 'PGM', it seems that the 'pgm' format is not in the modes
Thanks for the advice :)
As far as I can see scikit image uses the Python Imaging Library plugin for saving image files. PIL does not support PGM.
Refer to http://effbot.org/imagingbook/decoder.htm for how to write your own file decoder for PIL.
Extract:
import Image, ImageFile
import string
class SpamImageFile(ImageFile.ImageFile):
format = "SPAM"
format_description = "Spam raster image"
def _open(self):
# check header
header = self.fp.read(128)
if header[:4] != "SPAM":
raise SyntaxError, "not a SPAM file"
header = string.split(header)
# size in pixels (width, height)
self.size = int(header[1]), int(header[2])
# mode setting
bits = int(header[3])
if bits == 1:
self.mode = "1"
elif bits == 8:
self.mode = "L"
elif bits == 24:
self.mode = "RGB"
else:
raise SyntaxError, "unknown number of bits"
# data descriptor
self.tile = [
("raw", (0, 0) + self.size, 128, (self.mode, 0, 1))
]
Image.register_open("SPAM", SpamImageFile)
Image.register_extension("SPAM", ".spam")
Image.register_extension("SPAM", ".spa") # dos version