How to draw square pixel by pixel (Python, PIL) - python

On blank canvas I want to draw a square, pixel by pixel, using Pillow.
I have tried using img.putpixel((30,60), (155,155,55)) to draw one pixel but it doesn't do anything.
from PIL import Image
def newImg():
img = Image.new('RGB', (1280,768))
img.save('sqr.png')
return img
wallpaper = newImg()
wallpaper.show()

Running the code you say you have tried totally works, see below.
To draw the rectangle, repeat the img.putpixel((30,60), (155,155,55)) command with other coordinates.
from PIL import Image
def newImg():
img = Image.new('RGB', (100, 100))
img.putpixel((30,60), (155,155,55))
img.save('sqr.png')
return img
wallpaper = newImg()
wallpaper.show()
sqr.png

Related

how to fill the transparent part of an RGBA image with color, given the xy touch position?

i am trying to develop an android application in python that fills the area which is touched (exactly like the fill color tool in ms paint, which fills the closed area with color, or if not closed the color spreads everywhere).Given the xy position of touch, how can i fill the transparent area of a RGBA image using PIL or cv2, then save it?
given below is the pseudo code:
#rgb_value is the rgb value of the color with which to fill
#touchx, touchy are the xy positions of touch
#src is the source image, dst is the filename with which to save the output image
def fill_color(src, rgb_value, touchx, touchy, dst):
#code goes here
#then the image is saved
fill_color("freehand.png", [0,0,0], 5, 1000, "freehand(filled).png")
given below is freehand.png:(the patterned area is transparent)
cv2 has cv2.floodFill()
It will get color in start_point and search the same color in neighborhood and replace with color
import cv2
img_before = cv2.imread('image.png')
img_after = img_before.copy()
start_point = (400, 300)
color = (128, 128, 128)
cv2.floodFill(img_after, None, start_point, color)
cv2.imshow('before', img_before)
cv2.imshow('after', img_after)
cv2.waitKey(0)
cv2.destroyAllWindows()
but it works only with RGB but not RBGA so it would need more work.
pillow also has ImageDraw.floodfill() and it work with RGBA. If you work with numpy.array then it will need only to convert from array to Image and later back to array.
from PIL import Image, ImageDraw
img_before = Image.open('image.png')
img_after = img_before.copy()
start_point = (400, 300)
color = (128, 128, 128, 255)
img_draw = ImageDraw.floodfill(img_after, start_point, color, thresh=50)
img_before.show()
img_after.show()
#img_after.save('image_after.png')

Add border to an image with transparent background in Python Pillow Library

I am working with this image, which has a transparent background.
If I use the following code:-
def add_border(input_image, output_image, border, color=0):
img = Image.open(input_image)
if isinstance(border, int) or isinstance(border, tuple):
bimg = ImageOps.expand(img, border=border, fill=color)
else:
raise RuntimeError('Border is not an integer or tuple!')
bimg.save(output_image)
if __name__ == '__main__':
in_img = 'input/udit.png'
add_border(in_img, output_image='udit_border.png',
border=100, color="red")
This adds the border to the frame of the image.
I can find clear edges using the following code:-
from PIL import Image, ImageOps, ImageFilter
in_img = Image.open('input/udit.png')
image_with_edges = in_img.filter(ImageFilter.FIND_EDGES)
I was wondering, if I could draw a border across this edge and then join the two images to have a border on my image. But I couldn't find a way to do that. I want to change it into a sticker, so I am looking for something like the following image without bg.

How to change the color of a pixel using PIL?

I was trying to change pixel of an image in python using this question. If mode is 0, it changes first pixel in top right corner of image to grey(#C8C8C8). But it doesn't change. There is not enough documentation about draw.point(). What is the problem with this code?
import random
from PIL import Image, ImageDraw
mode = 0
image = Image.open("dom.jpg")
draw = ImageDraw.Draw(image)
width = image.size[0]
height = image.size[1]
pix = image.load()
string = "kod"
n = 0
if (mode == 0):
draw.point((0, 0), (200, 200, 200))
if(mode == 1):
print(pix[0,0][0])
image.save("dom.jpg", "JPEG")
del draw
Is using PIL a must in your case? If not then consider using OpenCV (cv2) for altering particular pixels of image.
Code which alter (0,0) pixel to (200,200,200) looks following way in opencv:
import cv2
img = cv2.imread('yourimage.jpg')
height = img.shape[0]
width = img.shape[1]
img[0][0] = [200,200,200]
cv2.imwrite('newimage.bmp',img)
Note that this code saves image in .bmp format - cv2 can also write .jpg images, but as jpg is generally lossy format, some small details might be lost. Keep in mind that in cv2 [0][0] is left upper corner and first value is y-coordinate of pixel, while second is x-coordinate, additionally color are three values from 0 to 255 (inclusive) in BGR order rather than RGB.
For OpenCV tutorials, including installation see this.

How can I insert Monospace fonts into an image with opencv?

Currently, I am able to insert some texts of HERSHEY font into images with openCV API (putText). But it seems openCV are not supporting any monospace font.
I was wondering how I can insert some Monospace or fixed-pitch texts into the image.
You could use PIL/Pillow for that aspect quite easily. OpenCV images are numpy arrays, so you can make a Pillow Image from an OpenCV image with:
PilImage = Image.fromarray(OpenCVimage)
Then you can draw with a mono spaced font using code in my answer here. You only need the 3 lines after the comment "Get a drawing context".
Then you can convert back to OpenCV image with:
OpenCVimage = np.array(PilImage)
That might look like this:
#!/usr/local/bin/python3
from PIL import Image, ImageFont, ImageDraw
import numpy as np
import cv2
# Open image with OpenCV
im_o = cv2.imread('start.png')
# Make into PIL Image
im_p = Image.fromarray(im_o)
# Get a drawing context
draw = ImageDraw.Draw(im_p)
monospace = ImageFont.truetype("/Library/Fonts/Andale Mono.ttf",32)
draw.text((40, 80),"Hopefully monospaced",(255,255,255),font=monospace)
# Convert back to OpenCV image and save
result_o = np.array(im_p)
cv2.imwrite('result.png', result_o)
Alternatively, you could have a function generate a lump of canvas itself, write your text on it, and then splice it into your OpenCV image wherever you want. Something along these lines - though I have no idea of what flexibility you would require so I have not parameterised everything:
#!/usr/local/bin/python3
from PIL import Image, ImageFont, ImageDraw, ImageColor
import numpy as np
import cv2
def GenerateText(size, fontsize, bg, fg, text):
"""Generate a piece of canvas and draw text on it"""
canvas = Image.new('RGB', size, bg)
# Get a drawing context
draw = ImageDraw.Draw(canvas)
monospace = ImageFont.truetype("/Library/Fonts/Andale Mono.ttf",fontsize)
draw.text((10, 10), text, fg, font=monospace)
# Change to BGR order for OpenCV's peculiarities
return cv2.cvtColor(np.array(canvas), cv2.COLOR_RGB2BGR)
# Open image with OpenCV
im_o = cv2.imread('start.png')
# Try some tests
w,h = 350,50
a,b = 20, 80
text = GenerateText((w,h), 32, 'black', 'magenta', "Magenta on black")
im_o[a:a+h, b:b+w] = text
w,h = 200,40
a,b = 120, 280
text = GenerateText((w,h), 18, 'cyan', 'blue', "Blue on cyan")
im_o[a:a+h, b:b+w] = text
cv2.imwrite('result.png', im_o)
Keywords: OpenCV, Python, Numpy, PIL, Pillow, image, image processing, monospace, font, fonts, fixed, fixed width, courier, HERSHEY.

Drawing semi-transparent polygons in PIL

How do you draw semi-transparent polygons using the Python Imaging Library?
Can you draw the polygon on a separate RGBA image then use the Image.paste(image, box, mask) method?
Edit: This works.
from PIL import Image
from PIL import ImageDraw
back = Image.new('RGBA', (512,512), (255,0,0,0))
poly = Image.new('RGBA', (512,512))
pdraw = ImageDraw.Draw(poly)
pdraw.polygon([(128,128),(384,384),(128,384),(384,128)],
fill=(255,255,255,127),outline=(255,255,255,255))
back.paste(poly,mask=poly)
back.show()
http://effbot.org/imagingbook/image.htm#image-paste-method
I think #Nick T's answer is good, but you need to be careful when using his code as written with a very large background image, especially in the case that you may be annotating several polygons on said image. This is something I do when processing huge satellite images with some object detection code and annotating the detections using a transparent rectangle. To make the code efficient no matter the size of the background image, I make the following suggestion.
I would modify the solution to specify that the polygon image that you will paste be only as large as required to hold the polygon, not the same size as the back image. The coordinates of the polygon are specified with respect to the local bounding box, not the global image coordinates. Then you paste the polygon image at the offset in the larger background image.
import Image
import ImageDraw
img_size = (512,512)
poly_size = (256,256)
poly_offset = (128,128) #location in larger image
back = Image.new('RGBA', img_size, (255,0,0,0) )
poly = Image.new('RGBA', poly_size )
pdraw = ImageDraw.Draw(poly)
pdraw.polygon([ (0,0), (256,256), (0,256), (256,0)],
fill=(255,255,255,127), outline=(255,255,255,255))
back.paste(poly, poly_offset, mask=poly)
back.show()
Using the Image.paste(image, box, mask) method will convert the alpha channel in the pasted area of the background image into the corresponding transparency value of the polygon image.
The Image.alpha_composite(im1,im2) method utilizes the alpha channel of the "pasted" image, and will not turn the background transparent. However, this method again needs two equally sized images.

Categories

Resources