I have Rectangle PNG, i want to process this image, remove some pixels to margin and save it in png format.
Here what i want at end;
Normal Image (There is no margin - border radius):
Here is my code; i tried somethings but not working properly;
qimg = QImage(PNG_YOL)
qimg = qimg.convertToFormat(QImage.Format_ARGB32) # making png or will be there black pixels
p = QPainter()
p.begin(qimg)
w = qimg.width() - 1
h=qimg.height() -1
for x in range(int(maxx)):
dx = 1 # i changed this value to get what i want, it works but not very fine, i am looking better way
for y in range(int(maxy)):
if x == 0:
qimg.setPixel(x, y, Qt.transparent)
qimg.setPixel(w - x, y, Qt.transparent)
if x != 0 and y < int(h * 1 / x / dx):
qimg.setPixel(x, y, Qt.transparent)
qimg.setPixel(w - x, y, Qt.transparent)
if x != 0:
qimg.setPixel(x, int(h * 1 / x / dx), Qt.transparent)
qimg.setPixel(w - x, int(h * 1 / x / dx), Qt.transparent)
p.end()
qimg.save(PNG_YOL)
With this code, i can get fine result but i am looking for better way.
Note: I just want only add margins to left-top and right-top.
Instead of getting too involved in processing pixel by pixel you can use QPainter with a QPainterPath:
from PyQt5 import QtCore, QtGui
qin = QtGui.QImage("input.png")
qout = QtGui.QImage(qin.size(), qin.format())
qout.fill(QtCore.Qt.transparent)
painter = QtGui.QPainter(qout)
path = QtGui.QPainterPath()
radius = 20
r = qout.rect()
path.arcMoveTo(0, 0, radius, radius, 180)
path.arcTo(0, 0, radius, radius, 180, -90)
path.arcTo(r.width()-radius, 0, radius, radius, 90, -90)
path.lineTo(r.bottomRight())
path.lineTo(r.bottomLeft())
path.closeSubpath()
painter.setClipPath(path)
painter.drawImage(QtCore.QPoint(0, 0), qin)
painter.end()
qout.save("output.png")
Related
Goal: export this output as a GIF.
This is working code, using Processing and Python.
This actually runs forever, how do I stop it after a while so as I can save as GIF?
import random
radius = 0
def setup():
global displayWidth, displayHeight, radius
size(displayWidth, displayHeight)
background(0)
noFill()
stroke(255, 25)
radius = height / 2
def draw():
global radius
center_x = width / 2
center_y = height / 2
beginShape()
for i in range(360):
_noise = noise(i * 0.02, float(frameCount) / 50)
x = center_x + radius * cos(radians(i)) * _noise
y = center_y + radius * sin(radians(i)) * _noise
curveVertex(x, y)
if radius == 0:
stroke(225, 0, 0, 10)
if radius == 100:
stroke(0, 225, 0, 25)
if radius == 200:
stroke (0, 0, 225, 25)
endShape(CLOSE)
radius -= 1
Please let me know if there is anything else I should add to post.
You can't. GIFs are not supported in processing. However you can use saveFrame() to saves a numbered sequence of images. Just call saveFrame() at the end of draw. There are numerous tools that can create a GIF from a list of images.
I just started learning tkinter by drawing a Mandelbrot set.
I specify a fill colour as the string '#0074bf', but it is rendered as black. Why is the kwarg ignored in the call c.create_oval(x, y, x, y, fill = '#0074bf')?
from tkinter import *
# Some globals
SIZE = 4
WIDTH = 600
HEIGHT = 600
ratio = (WIDTH / SIZE, HEIGHT / SIZE)
zmin = -SIZE / 2 - (SIZE / 2) * 1j
zmax = SIZE / 2 + (SIZE / 2) * 1j
ESCAPE_RADIUS = 4
max_iterations = 256
# Create the window with Canvas
master = Tk()
c = Canvas(master, width = WIDTH, height = HEIGHT)
c.pack()
# Define a function to iterate; here, the classic Mandelbrot set function, z -> z^2 + c
f = lambda z, c : z * z + c
def iterate(pixel):
"""
Given a pixel (as a complex number x + iy) return the
number of iterations it takes to escape,
or the final count if it doesn't.
"""
z0 = px_to_cx(pixel)
z = z0
num_iterations = 0
while abs(z) < ESCAPE_RADIUS and num_iterations < max_iterations:
z = f(z, z0)
num_iterations += 1
return num_iterations - 1
def px_to_cx(pixel):
return (pixel.real - WIDTH / 2) / ratio[0] + ((pixel.imag - HEIGHT / 2) / ratio[1]) * 1j
for y in range(HEIGHT):
for x in range(WIDTH):
num = iterate(x + y * 1j)
if num < max_iterations / 2:
# Here, the fill argument I supply seems to be ignored:
c.create_oval(x, y, x, y, fill = '#0074bf')
mainloop()
You are creating an oval one pixel wide and one pixel tall. What you see is the color of the oval outline. With only one pixel there isn't enough space to draw both the outline and an interior.
You can either set the outline attribute to the same color as the fill color, or set the outline width (width attribute) to zero.
Here is an example that shows two different blocks of 1-pixel ovals. One has the default outline width of one, and the other explicitly sets the outline width to zero. Notice in the first you're seeing the outline color, and in the second you're seeing the fill color.
import tkinter as tk
root = tk.Tk()
canvas = tk.Canvas(root, width=200, height=200, background="black")
canvas.pack(fill="both", expand=True)
for x in range(100):
for y in range(100):
canvas.create_oval(x, y, x, y, outline="red", fill="green")
for x in range(100, 200):
for y in range(100, 200):
canvas.create_oval(x, y, x, y, outline="red", fill="green", width=0)
root.mainloop()
In python, I have written some code that generates a circle using Bresenham's Midpoint Algorithm:
from PIL import Image, ImageDraw
radius = 100 #radius of circle
xpts = [] #array to hold x pts
ypts = [] #array to hold y pts
img = Image.new('RGB', (1000, 1000))
draw = ImageDraw.Draw(img) #to use draw.line()
pixels = img.load()
d = (5/4) - radius
x = 0
y = radius
xpts.append(x) #initial x value
ypts.append(y) #initial y value
while x < y:
if d < 0:
d += (2*x + 3)
x += 1
xpts.append(x + 500) #translate points to center by 500px
ypts.append(y - 500)
else:
d += (2 * (x - y) + 5)
x += 1
y -= 1
xpts.append(x + 500) #translate points to center by 500px
ypts.append(y - 500)
for i in range(len(xpts)): #draw initial and reflected octant points
pixels[xpts[i] ,ypts[i]] = (255,255,0) #initial octant
pixels[xpts[i],-ypts[i]] = (255,255,0)
pixels[-xpts[i],ypts[i]] = (255,255,0)
pixels[-xpts[i],-ypts[i]] = (255,255,0)
pixels[ypts[i],xpts[i]] = (255,255,0)
pixels[-ypts[i],xpts[i]] = (255,255,0)
pixels[ypts[i],-xpts[i]] = (255,255,0)
pixels[-ypts[i],-xpts[i]] = (255,255,0)
img.show()
To fill it, I had planned to use ImageDraw to draw a line horizontally within the circle from each point that is generated from the initial octant using draw.line(). I have the x and y coordinates stored in arrays. However, I am stuck interpreting each point and its reflection point to draw the horizontal line using draw.line(). Could someone clarify this?
Instead of drawing individual pixels, you would just add a line that connects the pixels corresponding to each other (either -x and +x or -y and +y). For each Bresenham step, you draw four lines (each connecting two octants).
Here is your adapted sample code. I dropped the points array and instead drew the lines directly. I also added the cx and cy variables that define the circle center. In your code, you sometimes used negative indices. This only works by coincidence because the circle is in the center:
from PIL import Image, ImageDraw
radius = 100 # radius of circle
xpts = [] # array to hold x pts
ypts = [] # array to hold y pts
img = Image.new('RGB', (1000, 1000))
draw = ImageDraw.Draw(img) # to use draw.line()
pixels = img.load()
d = (5 / 4) - radius
x = 0
y = radius
cx = 500
cy = 500
def draw_scanlines(x, y):
color = (255, 255, 0)
draw.line((cx - x, cy + y, cx + x, cy + y), fill=color)
draw.line((cx - x, cy - y, cx + x, cy - y), fill=color)
draw.line((cx - y, cy + x, cx + y, cy + x), fill=color)
draw.line((cx - y, cy - x, cx + y, cy - x), fill=color)
draw_scanlines(x, y)
while x < y:
if d < 0:
d += (2 * x + 3)
x += 1
else:
d += (2 * (x - y) + 5)
x += 1
y -= 1
draw_scanlines(x, y)
img.show()
Instead of drawing lines, you can fill all points inside a circle with radius radius in O(n^2) using:
# Your code here
for x in range(radius):
for y in range(radius):
if x**2 + y**2 < radius**2:
pixels[ x + 500 , y-500] = (255,255,0)
pixels[ x + 500 , -y-500] = (255,255,0)
pixels[ -x + 500 , y-500] = (255,255,0)
pixels[ -x + 500 , -y-500] = (255,255,0)
img.show()
How do I get ImageOps.fit(source28x32, (128, 128)) to fit without cropping off the top/bottom/sides? Do I really have to find the aspect, resize accordingly so the enlarged version does not exceed 128x128, and then add border pixels (or center the image in a 128x128 canvas)? Mind you that the source can be of any ratio, the 28x32 is just an example.
source image (28x32)
fitted image (128x128)
This is my attempt so far, not particularly elegant
def fit(im):
size = 128
x, y = im.size
ratio = float(x) / float(y)
if x > y:
x = size
y = size * 1 / ratio
else:
y = size
x = size * ratio
x, y = int(x), int(y)
im = im.resize((x, y))
new_im = Image.new('L', (size, size), 0)
new_im.paste(im, ((size - x) / 2, (size - y) / 2))
return new_im
New fitted image
Here is the function implemented in both PIL and cv2. The input can be of any size; the function finds the scale needed to fit the largest edge to the desired width, and then puts it onto a black square image of the desired width.
In PIL
def resize_PIL(im, output_edge):
scale = output_edge / max(im.size)
new = Image.new(im.mode, (output_edge, output_edge), (0, 0, 0))
paste = im.resize((int(im.width * scale), int(im.height * scale)), resample=Image.NEAREST)
new.paste(paste, (0, 0))
return new
In cv2
def resize_cv2(im, output_edge):
scale = output_edge / max(im.shape[:2])
new = np.zeros((output_edge, output_edge, 3), np.uint8)
paste = cv2.resize(im, None, fx=scale, fy=scale, interpolation=cv2.INTER_NEAREST)
new[:paste.shape[0], :paste.shape[1], :] = paste
return new
With a desired width of 128:
→
→
Not shown: these functions work on images larger than the desired size
This works pretty good to fit the image to size you want while filling in the rest with black space
from PIL import Image, ImageOps
def fit(im, width):
border = int((max(im.width, im.height) - min(im.width, im.height))/2)
im = ImageOps.expand(im, border)
im = ImageOps.fit(im, (width, width))
return im
I am struggling to draw a circle onto a rectangular png background. Whenever I try to draw a circle, it is stretched according to the scale of my image--making it an ellipse instead of a circle.
Here is the set up:
#set background image
surface = cairo.ImageSurface.create_from_png (image)
context = cairo.Context (surface)
WIDTH = cairo.ImageSurface.get_width(surface)
HEIGHT = cairo.ImageSurface.get_height(surface)
context.scale (WIDTH, HEIGHT)
And here is the circle drawing function, (x coord, y coord, radius, color value) and functions used for positioning:
def draw_node(cx,cy,r, rgb):
context.arc(x_scale(cx), y_scale(cy), r, 0, 2 * math.pi)
context.set_source_rgb(rgb[0]/255.0,rgb[1]/255.0,rgb[2]/255.0)
context.fill()
context.stroke()
def x_scale (x):
width = 1000.0
return (x + .5) / width
def y_scale (y):
height = 1000.0 * HEIGHT / WIDTH
return (y + .5) / height
I would particularly appreciate any advice on how to resolve the systemic scaling issue, but I would also appreciate advice on how to draw a circle within the confines of my existing setup.
Thank you very much.
there is probably no way to avoid that context.arc will draw an ellipse when your scaling is not symmetric. with your x_scale (x) function you only scale the center of the circle to be where it should be.
as a workaround i'd suggest to scale both directions by the same amount; sacrifice therefore that x_max and y_max will both be equal to 1.
i'd change in your code:
context.scale(HEIGHT, HEIGHT)
print('surface = 1 x {:1.2f}'.format(WIDTH/HEIGHT))
def draw_node(cx,cy,r, rgb):
context.arc(cx, cy, r, 0, 2 * math.pi)
context.set_source_rgb(rgb[0]/255.0,rgb[1]/255.0,rgb[2]/255.0)
context.fill()
context.stroke()
and not do any scalings (or scale by 1000 if that's what you prefer).