I need to remove all white-spaces from image but I don't know how to do it..
I am using trim functionality to trim white spaces from border but still white-spaces are present in middle of image I am attaching my original image from which I want to remove white-spaces
my code
from PIL import Image, ImageChops
import numpy
def trim(im):
bg = Image.new(im.mode, im.size, im.getpixel((0, 0)))
diff = ImageChops.difference(im, bg)
diff = ImageChops.add(diff, diff, 2.0, -100)
box = diff.getbbox()
if box:
im.crop(box).save("trim_pil.png")
im = Image.open("/home/einfochips/Documents/imagecomparsion/kroger_image_comparison/SnapshotImages/screenshot_Hide.png")
im = trim(im)
but this code only remove space from borders, I need to remove spaces from middle also. Please help if possible, it would be very good if I got all five images in different PNG file.
You could go the long way with a for loop
from PIL import Image, ImageChops
def getbox(im, color):
bg = Image.new(im.mode, im.size, color)
diff = ImageChops.difference(im, bg)
diff = ImageChops.add(diff, diff, 2.0, -100)
return diff.getbbox()
def split(im):
retur = []
emptyColor = im.getpixel((0, 0))
box = getbox(im, emptyColor)
width, height = im.size
pixels = im.getdata()
sub_start = 0
sub_width = 0
offset = box[1] * width
for x in range(width):
if pixels[x + offset] == emptyColor:
if sub_width > 0:
retur.append((sub_start, box[1], sub_width, box[3]))
sub_width = 0
sub_start = x + 1
else:
sub_width = x + 1
if sub_width > 0:
retur.append((sub_start, box[1], sub_width, box[3]))
return retur
This makes it easy to retrieve the crop boxes in the image like this:
im = Image.open("/home/einfochips/Documents/imagecomparsion/kroger_image_comparison/SnapshotImages/screenshot_Hide.png")
for idx, box in enumerate(split(im)):
im.crop(box).save("trim_{0}.png".format(idx))
If you already know the size of the images toy want to extract you could go with
def split(im, box):
retur = []
pixels = im.getdata()
emptyColor = pixels[0]
width, height = im.size;
y = 0;
while y < height - box[3]:
x = 0
y_step = 1
while x < width - box[2]:
x_step = 1
if pixels[y*width + x] != emptyColor:
retur.append((x, y, box[2] + x, box[3] + y))
y_step = box[3] + 1
x_step = box[2] + 1
x += x_step
y += y_step
return retur
Adding another parameter to the call
for idx, box in enumerate(split(im, (0, 0, 365, 150))):
im.crop(box).save("trim_{0}.png".format(idx))
Related
I am trying to crop an avatar and place it in a given location in another image using python pil.
Here is the output of what I have so far:
And here is the code:
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw
import textwrap
text = "Creating Twitter Cards dynamically with Python"
background_image = "data/obi-pvc.png" # this is the background
avatar = Image.open("data/avatar.png")
font = "data/fonts/AllertaStencil-Regular.ttf"
background = Image.open(background_image)
background_width, background_height = background.size
avatar.convert('RGBA')
## DO NOT change below this line!!
save_name = f"{text.lower().replace(' ', '_')}.png"
#textwrapped = textwrap.wrap(text, width=text_wrap_width)
# crop avatar
width, height = avatar.size
x = (width - height)//2
avatar_cropped = avatar.crop((x, 0, x+height, height))
width_cr, height_cr = avatar_cropped.size
# create grayscale image with white circle (255) on black background (0)
mask = Image.new('L', avatar_cropped.size)
mask_draw = ImageDraw.Draw(mask)
width, height = avatar_cropped.size
mask_draw.ellipse((0, 0, width, height), fill=255)
# add mask as alpha channel
avatar_cropped.putalpha(mask)
draw = ImageDraw.Draw(background)
font = ImageFont.truetype(font, font_size)
draw.text((offset,margin), '\n'.join(textwrapped), font=font, fill=color)
x, y = avatar_cropped.size
margin = 40
# left top
position_tl = (0 + margin, 0 + margin)
position_tr = (x - margin - width_cr, 0 + margin)
position_bl = (0 + margin, y - margin - height_cr)
position_br = (x - margin - width_cr, y - margin - height_cr)
background.paste(avatar_cropped, position)
background.save(f"data/output/{save_name}")
display(background)
The avatar should fit within the circle. I can't seem to really figure out how to apply the positioning. Thanks
Here is the avatar:
I want to draw evenly distributed random lines on a image.
My thought is to generate evenly distributed points on the image, and connect each two of them randomly. (Please ignore the bug of lacking coordinates on the 2 edges. I will fix them at last.)
from PIL import Image, ImageDraw
import random
# set attrs
gap = 170 # gap between points
err = 10 # volatility of coordinates
linewidth = 20 # line width
img = Image.open("img2.png", mode="r")
# initiation data/var
draw = ImageDraw.Draw(img)
width, height = img.size
class Coord:
def __init__(self, x, y):
self.x = x
self.y = y
currx = 0
curry = 0
coordlist = []
# generate the set of points
while currx <= width:
while curry <= height:
coordlist.append(Coord( \
currx + random.randint(-err,err), \
curry + random.randint(-err,err) \
))
curry += gap
curry = gap
currx += gap
# draw line between each two random points
while len(coordlist) >= 2:
# pick indices
index1 = random.randint(0, len(coordlist)-1)
index2 = random.randint(0, len(coordlist)-1)
while index1 == index2:
index2 = random.randint(0, len(coordlist)-1)
# draw line
draw.line((coordlist[index1].x,coordlist[index1].y, coordlist[index2].x,coordlist[index2].y), fill='black', width=linewidth)
# remove elements
coordlist = [v for i,v in enumerate(coordlist) if i not in frozenset((index1, index2))]
img.show()
However, this method is too inconsistent and sometimes some lines will stick together, causing some areas to be much more dense than other areas:
Good example:
Bad example:
I figured it out myself with help from the comments.
The idea is to set those evenly distributed grid points as the start points, and use a random angle with sin/cos to generate random lines.
async def process_img(gap: int, err: int, linewidth: int, url: str):
with urllib.request.urlopen(url) as webimg:
with open('temp.jpg', 'wb') as f:
f.write(webimg.read())
img = Image.open('temp.jpg')
# initiation data/var
draw = ImageDraw.Draw(img)
width, height = img.size
class Coord:
def __init__(self, x, y):
self.x = x
self.y = y
currx = 0
curry = 0
coordlist = []
# generate the set of points
while currx <= width:
while curry <= height:
coordlist.append(Coord( \
currx + random.randint(0,err), \
curry + random.randint(0,err) \
))
curry += gap
curry = gap
currx += gap
# calculate endpoint with angle/length
def calcEnd(x, y, angle, length):
endx = int(x - (math.cos(math.radians(angle)) * length))
endy = int(y - (math.sin(math.radians(angle)) * length))
return endx, endy
# draw line with random angle/length
for c in coordlist:
length = LENGTH
randangle = random.randint(0,359)
endx, endy = calcEnd(c.x, c.y, randangle, length)
draw.line((c.x, c.y, endx, endy), fill='black', width=linewidth)
img.convert('RGB').save('outtemp.jpg')
img_path = Path() / "outtemp.jpg"
return img_path.resolve()
To determine the class name of the detected object, I need to get the class_id of the image. The problem is, np.argmax always returns 0 and gets the first class name. When I detect another object, it should print class_id 1 but it prints 0 and I can't get the proper label name to display.
When I look at my .txt files, I see this:
0 0.170103 0.449807 0.319588 0.521236
1 0.266791 0.148936 0.496269 0.287234
2 0.265464 0.422780 0.510309 0.420849
def detect_img(self, img):
blob = cv2.dnn.blobFromImage(img, 0.00392 ,(416,416), (0,0,0), True, crop=False)
input_img = self.net.setInput(blob)
output = self.net.forward(self.output)
height, width, channel = img.shape
boxes = []
trusts = []
class_ids = []
for out in output:
for detect in out:
total_scores = detect[5:]
class_id = np.argmax(total_scores)
print(np.argmax(detect))
trust_factor = total_scores[class_id]
if trust_factor > 0.2:
x_center = int(detect[0] * width)
y_center = int(detect[1] * height)
w = int(detect[2] * width)
h = int(detect[3] * height)
x = int(x_center - w / 2)
y = int(x_center - h / 2)
boxes.append([x,y,w,h])
trusts.append(float(trust_factor))
class_ids.append(class_id)
for index in range(len(boxes)):
# if index in indexes:
x,y,w,h = boxes[index]
label = self.classes[class_ids[index]]
# always self.classes[0], self.classes=['samsung','iphone'] so result is always samsung
trust = round(trusts[index], 2)
text = f"{label}, Trust: {trust}"
cv2.rectangle(img, (x,y), (x + w, y + h), (0,255,0), 2)
cv2.putText(img, text, (x - 20, y + 40), cv2.FONT_HERSHEY_PLAIN, 1, (0,0,255), 2)
Python noob needs some help guys! Can someone show me how to rewrite my code using loops? Tried some different syntaxes but did not seem to work!
img = cv2.imread("C://Users//user//Desktop//research//images//Underwater_Caustics//set1//set1_color_0001.png")
tile11=img[1:640, 1:360]
cv2.imwrite('tile11_underwater_caustic_set1_0001.png', tile11)
tile12=img[641:1280, 1:360]
cv2.imwrite('tile12_underwater_caustic_set1_0001.png', tile12)
tile13=img[1281:1920, 1:360]
cv2.imwrite('tile13_underwater_caustic_set1_0001.png', tile13)
tile21=img[1:640, 361:720]
cv2.imwrite('tile21_underwater_caustic_set1_0001.png', tile21)
tile22=img[641:1280, 361:720]
cv2.imwrite('tile22_underwater_caustic_set1_0001.png', tile22)
tile23=img[1281:1920, 361:720]
cv2.imwrite('tile23_underwater_caustic_set1_0001.png', tile23)
tile31=img[1:640, 721:1080]
cv2.imwrite('tile31_underwater_caustic_set1_0001.png', tile31)
tile32=img[641:1280, 721:1080]
cv2.imwrite('tile32_underwater_caustic_set1_0001.png', tile32)
tile33=img[1281:1920, 721:1080]
cv2.imwrite('tile33_underwater_caustic_set1_0001.png', tile33)
As you can see, the image will be cut into 9 equal-size pieces, how to write it using loops?
This won't produce the same result like your code, but will give you some ideas:
img = cv2.imread('sample.jpg')
numrows, numcols = 4, 4
height = int(img.shape[0] / numrows)
width = int(img.shape[1] / numcols)
for row in range(numrows):
for col in range(numcols):
y0 = row * height
y1 = y0 + height
x0 = col * width
x1 = x0 + width
cv2.imwrite('tile_%d%d.jpg' % (row, col), img[y0:y1, x0:x1])
I needed image tiling where last parts or edge tiles are required to be full tile images.
Here is the code I use:
import cv2
import math
import os
Path = "FullImage.tif";
filename, file_extension = os.path.splitext(Path)
image = cv2.imread(Path, 0)
tileSizeX = 256;
tileSizeY = 256;
numTilesX = math.ceil(image.shape[1]/tileSizeX)
numTilesY = math.ceil(image.shape[0]/tileSizeY)
makeLastPartFull = True; # in case you need even siez
for nTileX in range(numTilesX):
for nTileY in range(numTilesY):
startX = nTileX*tileSizeX
endX = startX + tileSizeX
startY = nTileY*tileSizeY
endY = startY + tileSizeY;
if(endY > image.shape[0]):
endY = image.shape[0]
if(endX > image.shape[1]):
endX = image.shape[1]
if( makeLastPartFull == True and (nTileX == numTilesX-1 or nTileY == numTilesY-1) ):
startX = endX - tileSizeX
startY = endY - tileSizeY
currentTile = image[startY:endY, startX:endX]
cv2.imwrite(filename + '_%d_%d' % (nTileY, nTileX) + file_extension, currentTile)
This is for massive image reconstruction using part of flowfree his code. By using a folder of sliced images in the same area the script is, you can rebuild the image. I hope this helps.
import cv2
import glob
import os
dir = "."
pathname = os.path.join(dir, "*" + ".png")
images = [cv2.imread(img) for img in glob.glob(pathname)]
img = images[0]
numrows, numcols = 1,1
height = int(img.shape[0] / numrows)
width = int(img.shape[1] / numcols)
for row in range(numrows):
for col in range(numcols):
y0 = row * height
y1 = y0 + height
x0 = col * width
x1 = x0 + width
cv2.imwrite('merged_img_%d%d.jpg' % (row, col), img[y0:y1, x0:x1])
I have no experience with python, but the owner of this script is not responding.
When I drag my photos over this script, to create a montage, it ends up cutting off half of the last photo on the right side edge.
Being 4 pictures wide,
1 2 3 4
5 6 7 8
Pictures 4 and 8 usually get halved. The space is there for the pictures (its blank though)
I was wondering what would be causing this.
I have thought it is possible that it was cropping, but its almost like the half of the picture isn't imported or detected.
Well, you drag selected photos over the script , it outputs something like this
So you can take a bunch of photos or screenshots, and combine them into one single file, easily instead of adding each photo individually.
Size of each photo is roughly 500x250 at max.
EDIT:
Here is the upload of the preview, as you can see the images have the slots, but they are "disappearing" if that makes sense.
EDIT2:
This script has worked at one time, I haven't edited it or anything. It had worked on a ~70 screenshot montage. No errors or anything. Is there something that my computer could be doing to disrupt the importing of the images?
#!/usr/bin/env python
import os
import sys
from time import strftime
import Image
import ImageDraw
import ImageFont
# parameters
row_size = 4
margin = 3
def generate_montage(filenames):
images = [Image.open(filename) for filename in filenames]
width = 0
height = 0
i = 0
sum_x = max_y = 0
width = max(image.size[1]+margin for image in images)*row_size
height = sum(image.size[0]+margin for image in images)
montage = Image.new(mode='RGBA', size=(width, height), color=(0,0,0,0))
try:
image_font = ImageFont.truetype('font/Helvetica.ttf', 18)
except:
try:
image_font = ImageFont.load('font/Helvetica-18.pil')
except:
image_font = ImageFont.load_default()
draw = ImageDraw.Draw(montage)
offset_x = offset_y = 0
i = 0
max_y = 0
max_x = 0
offset_x = 0
for image in images:
montage.paste(image, (offset_x, offset_y))
text_coords = offset_x + image.size[0] - 45, offset_y + 120
draw.text(text_coords, '#{0}'.format(i+1), font=image_font)
max_x = max(max_x, offset_x+image.size[0])
if i % row_size == row_size-1:
offset_y += max_y+margin
max_y = 0
offset_x = 0
else:
offset_x += image.size[0]+margin
max_y = max(max_y, image.size[1])
i += 1
if i % row_size:
offset_y += max_y
filename = strftime("Montage %Y-%m-%d at %H.%M.%S.png")
montage = montage.crop((0, 0, max_x, offset_y))
montage.save(filename)
if __name__ == '__main__':
old_cwd = os.getcwd()
os.chdir(os.path.dirname(sys.argv[0]))
try:
if len(sys.argv) > 1:
generate_montage(sys.argv[1:])
finally:
os.chdir(old_cwd)
In the size calculation, you use image.size[1] for the width, but that's the height! Use image.size[0] for the width and image.size[1] for the height instead.
Also, a couple of minor stylistic notes:
Do you really need the script to always run from the program's directory? In any case, os.chdir(os.path.dirname(sys.argv[0])) prevents the program from being executed as ./montage.py, so you may want to use a abspath to allow the invocation from the current directory.
Instead of having to update the counter i, you can change the for loop to
for i,image in enumerate(images):
The following lines have no effect, since the variables are overwritten / never used:
width = 0
height = 0
i = 0
sum_x = max_y = 0
All in all, the code could look like this:
#!/usr/bin/env python
import os.path
import sys
from time import strftime
import Image
row_size = 4
margin = 3
def generate_montage(filenames, output_fn):
images = [Image.open(filename) for filename in filenames]
width = max(image.size[0] + margin for image in images)*row_size
height = sum(image.size[1] + margin for image in images)
montage = Image.new(mode='RGBA', size=(width, height), color=(0,0,0,0))
max_x = 0
max_y = 0
offset_x = 0
offset_y = 0
for i,image in enumerate(images):
montage.paste(image, (offset_x, offset_y))
max_x = max(max_x, offset_x + image.size[0])
max_y = max(max_y, offset_y + image.size[1])
if i % row_size == row_size-1:
offset_y = max_y + margin
offset_x = 0
else:
offset_x += margin + image.size[0]
montage = montage.crop((0, 0, max_x, max_y))
montage.save(output_fn)
if __name__ == '__main__':
basename = strftime("Montage %Y-%m-%d at %H.%M.%S.png")
exedir = os.path.dirname(os.path.abspath(sys.argv[0]))
filename = os.path.join(exedir, basename)
generate_montage(sys.argv[1:], filename)