So I've been trying to make a meme bot, in python using PILLOW (a PIL fork) basically it takes a random template and a random source image and puts them together.
So far I managed to make a version which puts the source image adequately in the template, but fails to resize it. Here's my code:
P. S. I mainly code in C, so my python ain't that great
from PIL import Image, ImageOps
import os, random
import re
import linecache
import string
temp_dir = "temp dir"
source_dir = "source dir"
memes_dir = "memes dir"
#Random template & source image
rand_temp = random.choice(os.listdir(temp_dir))
rand_source = random.choice(os.listdir(source_dir))
#template
bot = Image.open(temp_dir + rand_temp)
#source image
top = Image.open(source_dir + rand_source)
width, height = bot.size
size = (width, height)
#Puts Source image in template
meme = ImageOps.fit(top, size, Image.ANTIALIAS)
meme.paste(bot, (0,0), bot)
meme.show()
Is there any way to achieve what I want? Or should I maybe move to another language? Thanks!
Related
I want to add a white background to my transparant images (png) and resize them. The images are located in a folder. I need to do bulk work, not 1 image at the time.
I removed the background from the images first with rembg (works good) and now I want to change the images.
My code
import rembg
import glob
from pathlib import Path
from rembg import remove, new_session
session = new_session()
for file in Path(r'C:\test\images').glob('*.jpg'):
input_path = str(file)
output_path = str(file.parent / (file.stem + ".out.png"))
with open(input_path, 'rb') as i:
with open(output_path, 'wb') as o:
input = i.read()
output = remove(input, session=session)
o.write(output)
I do not know how to add the white backgroud and resize with python because I'm fairly new to this. Thank you in advance!
I think you want a helper function to do the work, something like:
from PIL import Image
import rembg
def process(session, image, *, size=None, bgcolor='white'):
"session is a rembg Session, and image is a PIL Image"
if size is not None:
image = image.resize(size)
else:
size = image.size
result = Image.new("RGB", size, bgcolor)
out = rembg.remove(image, session=session)
result.paste(out, mask=out)
return result
The idea being that you pass a rembg Session and a Pillow Image in and it will remove the background and flatten that image, resizing along the way.
As a working example, you could do something like:
from io import BytesIO
import requests
session = rembg.new_session("u2netp")
res = requests.get("https://picsum.photos/600")
res.raise_for_status()
with Image.open(BytesIO(res.content)) as img:
out = process(session, img, size=(256, 256), bgcolor='#F0E68C')
out.save("output.png")
For example, an input and output might be:
If you wanted to work with lots of files, your pathlib objects can be passed directly to Pillow:
from pathlib import Path
for path_in in Path(r'C:\test\images').glob('*.jpg'):
path_out = path_in.parent / f"{path_in.stem}-out.png"
# no point processing images that have already been done!
if path_out.exists():
continue
with Image.open(path_in) as img:
out = process(session, img, size=(256, 256), bgcolor='#F0E68C')
out.save(path_out)
Update: it's often worth adding a check into these loops so they can be rerun and not have to process everything again. If you really do want images to be re-processed then just delete *-out.png
from PIL import Image, ImageDraw, ImageFont
import glob
import os
images = glob.glob("directory_path/*.jpg")
for img in images:
images = Image.open(img)
draw = ImageDraw.Draw(images)
font = ImageFont.load_default() #Downloaded Font from Google font
text = "Text on all images from directory"
draw.text((0,150),text,(250,250,250),font=font)
images.save(img)
I have to put text on all images , I have tried above code but its not working
This code worked for me just fine, but the text was hard to read because it was small and white. I did change directory_path to images and put my images in there. The images looked like this, the text is small and on the left side:
Here is the solution
from PIL import Image,ImageDraw,ImageFont
import glob
import os
images=glob.glob("path/*.jpg")
for img in images:
images=Image.open(img)
draw=ImageDraw.Draw(images)
font=ImageFont.load_default()
text="Whatever text"
draw.text((0,240),text,(250,250,250),font=font)
images.save(img)
one possible problem with the code may be that you are using the images variable for saving the list of images and also to iterate through the images.
Try this code, this will work for sure.
from PIL import Image, ImageDraw, ImageFont
import glob
import os
images = glob.glob("new_dir/*.jpg")
print(images)
for img in images:
image = Image.open(img)
draw = ImageDraw.Draw(image)
font = ImageFont.load_default() #Downloaded Font from Google font
text = "Text on all images from directory"
draw.text((0,150),text,fill = 'red' ,font=font)
image.save(img)
In these lines of code, I create a small red image and save it as "new_image.png". But I can't find the saved image, where is it saved? And can I change the place where I want to save my image?
from PIL import Image
img = Image.new('RGB', (60, 30), color = 'red')
img.save("new_image.PNG")
I tested your code:
from PIL import Image
img = Image.new('RGB', (60, 30), color = 'red')
img.save("new_image.PNG")
It works well for me:
The reason may because your current work path is not as you thought.
See my answer here:
https://stackoverflow.com/a/66449241/12838403
If you don't specify a path, the image is saved in the same path as the python file generating the image
from PIL import Image
img = Image.new('RGB', (60, 30), color = 'red')
img.save("new_image.PNG")
To save image in a directory you specify, you can use
from PIL import Image
import os
image_path = "path/to/image"
image = image.save(f"{image_path}/image.png")
Note: If this directory does not exist, you would have to create it before saving your image in it.
from PIL import Image
import os
image_path = "path/to/image"
os.mkdir(image_path)
image = image.save(f"{image_path}/image.png")
I would like to extract text from scanned PDFs.
My "test" code is as follows:
from pdf2image import convert_from_path
from pytesseract import image_to_string
from PIL import Image
converted_scan = convert_from_path('test.pdf', 500)
for i in converted_scan:
i.save('scan_image.png', 'png')
text = image_to_string(Image.open('scan_image.png'))
with open('scan_text_output.txt', 'w') as outfile:
outfile.write(text.replace('\n\n', '\n'))
I would like to know if there is a way to extract the content of the image directly from the object converted_scan, without saving the scan as a new "physical" image file on the disk?
Basically, I would like to skip this part:
for i in converted_scan:
i.save('scan_image.png', 'png')
I have a few thousands scans to extract text from. Although all the generated new image files are not particularly heavy, it's not negligible and I find it a bit overkill.
EDIT
Here's a slightly different, more compact approach than Colonder's answer, based on this post. For .pdf files with many pages, it might be worth adding a progress bar to each loop using e.g. the tqdm module.
from wand.image import Image as w_img
from PIL import Image as p_img
import pyocr.builders
import regex, pyocr, io
infile = 'my_file.pdf'
tool = pyocr.get_available_tools()[0]
tool = tools[0]
req_image = []
txt = ''
# to convert pdf to img and extract text
with w_img(filename = infile, resolution = 200) as scan:
image_png = scan.convert('png')
for i in image_png.sequence:
img_page = w_img(image = i)
req_image.append(img_page.make_blob('png'))
for i in req_image:
content = tool.image_to_string(
p_img.open(io.BytesIO(i)),
lang = tool.get_available_languages()[0],
builder = pyocr.builders.TextBuilder()
)
txt += content
# to save the output as a .txt file
with open(infile[:-4] + '.txt', 'w') as outfile:
full_txt = regex.sub(r'\n+', '\n', txt)
outfile.write(full_txt)
UPDATE MAY 2021
I realized that although pdf2image is simply calling a subprocess, one doesn't have to save images to subsequently OCR them. What you can do is just simply (you can use pytesseract as OCR library as well)
from pdf2image import convert_from_path
for img in convert_from_path("some_pdf.pdf", 300):
txt = tool.image_to_string(img,
lang=lang,
builder=pyocr.builders.TextBuilder())
EDIT: you can also try and use pdftotext library
pdf2image is a simple wrapper around pdftoppm and pdftocairo. It internally does nothing more but calls subprocess. This script should do what you want, but you need a wand library as well as pyocr (I think this is a matter of preference, so feel free to use any library for text extraction you want).
from PIL import Image as Pimage, ImageDraw
from wand.image import Image as Wimage
import sys
import numpy as np
from io import BytesIO
import pyocr
import pyocr.builders
def _convert_pdf2jpg(in_file_path: str, resolution: int=300) -> Pimage:
"""
Convert PDF file to JPG
:param in_file_path: path of pdf file to convert
:param resolution: resolution with which to read the PDF file
:return: PIL Image
"""
with Wimage(filename=in_file_path, resolution=resolution).convert("jpg") as all_pages:
for page in all_pages.sequence:
with Wimage(page) as single_page_image:
# transform wand image to bytes in order to transform it into PIL image
yield Pimage.open(BytesIO(bytearray(single_page_image.make_blob(format="jpeg"))))
tools = pyocr.get_available_tools()
if len(tools) == 0:
print("No OCR tool found")
sys.exit(1)
# The tools are returned in the recommended order of usage
tool = tools[0]
print("Will use tool '%s'" % (tool.get_name()))
# Ex: Will use tool 'libtesseract'
langs = tool.get_available_languages()
print("Available languages: %s" % ", ".join(langs))
lang = langs[0]
print("Will use lang '%s'" % (lang))
# Ex: Will use lang 'fra'
# Note that languages are NOT sorted in any way. Please refer
# to the system locale settings for the default language
# to use.
for img in _convert_pdf2jpg("some_pdf.pdf"):
txt = tool.image_to_string(img,
lang=lang,
builder=pyocr.builders.TextBuilder())
Now, I have a python game that has sprites, and it obtains the images from files in its directory. I want to make it such that I do not even need the files. Somehow, to pre-store the image in a variable so that i can call it from within the program, without the help of the additional .gif files
The actual way i am using the image is
image = PIL.Image.open('image.gif')
So it would be helpful if you could be precise about how to replace this code
Continuing #eatmeimadanish's thoughts, you can do it manually:
import base64
with open('image.gif', 'rb') as imagefile:
base64string = base64.b64encode(imagefile.read()).decode('ascii')
print(base64string) # print base64string to console
# Will look something like:
# iVBORw0KGgoAAAANS ... qQMAAAAASUVORK5CYII=
# or save it to a file
with open('testfile.txt', 'w') as outputfile:
outputfile.write(base64string)
# Then make a simple test program:
from tkinter import *
root = Tk()
# Paste the ascii representation into the program
photo = 'iVBORw0KGgoAAAANS ... qQMAAAAASUVORK5CYII='
img = PhotoImage(data=photo)
label = Label(root, image=img).pack()
This is with tkinter PhotoImage though, but I'm sure you can figure out how to make it work with PIL.
Here is how you can open it using PIL. You need a bytes representation of it, then PIL can open a file like object of it.
import base64
from PIL import Image
import io
with open("picture.png", "rb") as file:
img = base64.b64encode(file.read())
img = Image.open(io.BytesIO(img))
img.show()