Copying a triangular area from one picture to another in Python - python

I am trying to write a python function that will copy a triangular area from anywhere on a picture to a new blank picture. I can copy a rectangular area from a picture to a new empty picture, but I just don't know how to copy a triangle. That's what I have, but it only copies a rectangular area. Sorry if it looks messy or over-complicated, but I'm just starting how to write in python.
def copyTriangle():
file=pickAFile()
oldPic=makePicture(file)
newPic=makeEmptyPicture(getWidth(oldPic),getHeight(oldPic))
xstart=getWidth(oldPic)/2
ystart=getHeight(oldPic)/2
for y in range(ystart,getHeight(oldPic)):
for x in range(xstart,getWidth(oldPic)):
oldPixel=getPixel(oldPic,x,y)
colour=getColor(oldPixel)
newPixel=getPixel(newPic,x,y)
setColor(newPixel,colour)

Function to copy a triangular area from one pic to another.
def selectTriangle(pic):
w= getWidth (pic)
h = getHeight(pic)
newPic = makeEmptyPicture(w,h)
x0=107#test point 0
y0=44
x1=52#test point 1
y1=177
x2=273 #test point 2
y2=216
#(y-y0)/(y1-y0)=(x-x0)/(x1-x0)
for y in range (0,h):
for x in range (0, w):
#finding pixels within the plotted lines between eat set of points
if (x>((y-y0)*(x1-x0)/(y1-y0)+x0) and x<((y-y0)*(x2-x0)/(y2-y0)+x0) and x>((y-y2)*(x1-x2)/(y1-y2)+x2)):
pxl = getPixel(pic, x, y)
newPxl= getPixel(newPic,x,y)
color = getColor(pxl)
setColor (newPxl, color)
return (newPic)

If you're willing to do it pixel by pixel as in your example, then just copy the pixels of the triangle. Mostly this depends on how you want to define the triangle.
The simplest triangle is to make your x range (inner loop) dependent on your y-value (outer loop), like:
for y in range(ystart, ystart+getHeight(oldPic)):
for x in range(xstart, xstart + int( getWidth(oldPic)*((y-ystart)/float(getHeight)):
More generally, you could still keep your same x and y loops, and then put the copying commands in an if block, where you check whether the point is in your triangle.
Beyond this, there are much more efficient ways of doing this, using masks, etc.
Note, also here I changed the y-range to range(ystart, ystart+getHeight(oldPic)), which I think is probably what you want for a height that doesn't depend on the starting position.

Related

Python-Creating List Of Grid Positions Within A Certain Bound Given A Distance

two scenarios
I have an x axis size, a y axis size, a render distance, a list of grid position numbers and a central grid position.
I am trying to create a list of all the grid positions within the render distance of a central grid position.
The size of the x and y axis may be different independently. Optimally this algorithm would not attempt to get positions where the render distance extends over the side of the x or y axis.
Thanks.
I'm writing this to help you answer your own question in the style that I would go about it. As with anything in coding, what you need to do is be able to break down a big problem into multiple smaller ones.
Design two functions to convert to and from (x, y) coordinates (optional, it'll make your life easier, but won't be as efficient, personally I would avoid this for a bit of a challenge).
Given n, size and distance, calculate up, down, left and right. If the size is different for different axis, then just provide the function with the correct one.
eg.
def right(n, size, distance):
return n + size * distance
def down(n, size, distance):
return n - distance
Given size, make sure the above functions don't go off the edge of the grid. Converting the points to (x, y) coordinates for this part may help.
Now you have the sides of the square, run the functions again to get the corners. For example, to get the top right corner, you could do right(up(*args)) or up(right(*args))
With the corners, you can now calculate what's in your square. Converting the points to (x, y) coordinates will make it easier.

Using Loops to Create Iterations of Graphic Objects

I have looked around for an answer to this but cannot seem to find a solution. I'd like the word function to create 10 identical circles with center points that have the same y coordinates but different x coordinates so that their spacing is 25 from one center point to the other. The functions that I have created are only drawing one iteration of the object and I cannot figure out how to fix this. Thank you for any help.
I have tried to create two separate functions. One defines the loop function that I would like to print 10 circles while appending a list of circles. The other function calls upon the draw function to draw all 13 circles.
def draw(window):
circles=[]
for i in range(10):
x=25
circle=Circle(Point(0+x,370),10)
circle.setFill("yellow")
circles.append(circle)
circle.draw(window)
return circles
def circleRow():
window=GraphWin("Window",400,400)
window.setBackground("red")
cicles1=draw(window)
circleRow()
I expected an output of 10 circles evenly spaced along the same y-coordinate but am only getting a single circle.
We could replace the (unused) iteration variable i with x instead and explicitly describe what range of values it should take on:
from graphics import *
def draw(window):
circles = []
for x in range(25, 275, 25):
circle = Circle(Point(x, 370), 10)
circle.setFill('yellow')
circle.draw(window)
circles.append(circle)
return circles
def circleRow():
window = GraphWin("Window", 400, 400)
window.setBackground('red')
circles = draw(window)
window.getMouse()
window.close()
circleRow()
There are various other valid ways to approach this.

Python/Psychopy: checking if a point is within a circle

I want to know the most efficient way to check if a given point (an eye coordinate) is within a specific region (in this case a circle).
Code:
win = visual.Window([600,600], allowGUI=False)
coordinate = [50,70] #example x and y coordinates
shape = visual.Circle(win, radius=120, units='pix') #shape to check if coordinates are within it
if coordinate in shape:
print "inside"
else:
print "outside"
>>TypeError: argument of type 'Circle' is not iterable
My x and y coordinates correspond to one point on the window, I need to check if this point falls within the circle whose radius is 120 pixels.
Thanks,
Steve
PsychoPy's ShapeStim classes have a .contains() method, as per the API:
http://psychopy.org/api/visual/shapestim.html#psychopy.visual.ShapeStim.contains
So your code could simply be:
if shape.contains(coordinate):
print 'inside'
else:
print 'outside'
Using this method has the advantage that it is a general solution (taking into account the shape of the stimulus vertices) and is not just a check on the pythagorean distance from the stimulus centre (which is a special case for circles only).
I don't think it needs to be that complicated:
center=(600,600)
radius=120
coordinate=(50,70)
if (coordinate[0]-center[0])**2 + (coordinate[1]-center[1])**2 < radius**2:
print "inside"
else:
print "outside"

Pixels and geometrical shapes - Python/PIL

I'm trying to build a basic heatmap based on points. Each point has a heat radius, and therefore is represented by a circle.
Problem is that the circle needs to be converted in a list of pixels colored based on the distance from the circle's center.
Finding it hard to find an optimal solution for many points, what I have for now is something similar to this:
for pixels in pixels:
if (pixel.x - circle.x)**2 + (pixel.y - circle.y)**2 <= circle.radius:
pixel.set_color(circle.color)
Edit:
data I have:
pixel at the center of the circle
circle radius (integer)
Any tips?
Instead of doing it pixel-by-pixel, use a higher level interface with anti-aliasing, like the aggdraw module and its ellipse(xy, pen, brush) function.
Loop over the number of color steps you want (lets say, radius/2) and use 255/number_of_steps*current_step as the alpha value for the fill color.
For plotting it is usually recommended to use the matplotlib library (e.g. using imshow for heatmaps). Of course matplotlib also supports color gradients.
However, I don't really understand what you are trying to accomplish. If you just want to draw a bunch of colored circles then pretty much any graphics library will do (e.g. using the ellipse function in PIL).
It sounds like you want to color the pixel according to their distance from the center, but your own example code suggests that the color is constant?
If you are handling your pixels by yourself and your point is to increase performances, you can just focus on the square [x - radius; x + radius] * [y - radius; y + radius] since the points of your circle live here. That will save you a lot of useless iterations, if of course you CAN focus on this region (i.e. your pixels are not just an array without index per line and column).
You can even be sure that the pixels in the square [x - radius*sqrt(2)/2; x + radius*sqrt(2)/2] * [y - radius*sqrt(2)/2; y + radius*sqrt(2)/2] must be colored, with basic trigonometry (maximum square inside the circle).
So you could do:
import math
half_sqrt = math.sqrt(2) / 2
x_max = x + half_sqrt
y_max = y + half_sqrt
for (i in range(x, x + radius + 1):
for (j in range(y, y + radius + 1):
if (x <= x_max and y <= y_max):
colorize_4_parts(i, j)
else:
pixel = get_pixel(i, j)
if (pixel.x - circle.x)**2 + (pixel.y - circle.y)**2 <= circle.radius:
# Apply same colors as above, could be a function
colorize_4_parts(i, j)
def colorize_4_parts(i, j):
# Hoping you have access to such a function get_pixel !
pixel_top_right = get_pixel(i, j)
pixel_top_right.set_color(circle.color)
pixel_top_left = get_pixel(2 * x - i, j)
pixel_top_leftt.set_color(circle.color)
pixel_bot_right = get_pixel(i, 2 * y - j)
pixel_bot_right.set_color(circle.color)
pixel_bot_left = get_pixel(2 * x - i, 2 * y - j)
pixel_bot_leftt.set_color(circle.color)
This is optimized to reduce costly computations to the minimum.
EDIT: function updated to be more efficient again: I had forgotten that we had a double symetry horizontal and vertical, so we can compute only for the top right corner !
This is a very common operation, and here's how people do it...
summary: Represent the point density on a grid, smooth this using a 2D convolution if needed (this gives your points to circles), and plot this as a heatmap using matplotlib.
In more detail: First, make a 2D grid for your heatmap, and add your data points to the grid, incrementing by the cells by 1 when a data point lands in the cell. Second, make another grid to represents the shape you want to give each point (usually people use a cylinder or gaussian or something like this). Third, convolve these two together, using, say scipy.signal.convolve2d. Finally, use matplotlib's imshow function to plot the convolution, and this will be your heatmap.
If you can't use the tools suggested in the standard approach then you might find work-arounds, but it has advantages. For example, the convolution will deal well with cases when the circles overlap.

How to 'zoom' in on a section of the Mandelbrot set?

I have created a Python file to generate a Mandelbrot set image. The original maths code was not mine, so I do not understand it - I only heavily modified it to make it about 250x faster (Threads rule!).
Anyway, I was wondering how I could modify the maths part of the code to make it render one specific bit. Here is the maths part:
for y in xrange(size[1]):
coords = (uleft[0] + (x/size[0]) * (xwidth),uleft[1] - (y/size[1]) * (ywidth))
z = complex(coords[0],coords[1])
o = complex(0,0)
dotcolor = 0 # default, convergent
for trials in xrange(n):
if abs(o) <= 2.0:
o = o**2 + z
else:
dotcolor = trials
break # diverged
im.putpixel((x,y),dotcolor)
And the size definitions:
size1 = 500
size2 = 500
n=64
box=((-2,1.25),(0.5,-1.25))
plus = size[1]+size[0]
uleft = box[0]
lright = box[1]
xwidth = lright[0] - uleft[0]
ywidth = uleft[1] - lright[1]
what do I need to modify to make it render a certain section of the set?
The line:
box=((-2,1.25),(0.5,-1.25))
is the bit that defines the area of coordinate space that is being rendered, so you just need to change this line. First coordinate pair is the top-left of the area, the second is the bottom right.
To get a new coordinate from the image should be quite straightforward. You've got two coordinate systems, your "image" system 100x100 pixels in size, origin at (0,0). And your "complex" plane coordinate system defined by "box". For X:
X_complex=X_complex_origin+(X_image/X_image_width)*X_complex_width
The key in understanding how to do this is to understand what the coords = line is doing:
coords = (uleft[0] + (x/size[0]) * (xwidth),uleft[1] - (y/size[1]) * (ywidth))
Effectively, the x and y values you are looping through which correspond to the coordinates of the on-screen pixel are being translated to the corresponding point on the complex plane being looked at. This means that (0,0) screen coordinate will translate to the upper left region being looked at (-2,1.25), and (1,0) will be the same, but moved 1/500 of the distance (assuming a 500 pixel width window) between the -2 and 0.5 x-coordinate.
That's exactly what that line is doing - I'll expand just the X-coordinate bit with more illustrative variable names to indicate this:
mandel_x = mandel_start_x + (screen_x / screen_width) * mandel_width
(The mandel_ variables refer to the coordinates on the complex plane, the screen_ variables refer to the on-screen coordinates of the pixel being plotted.)
If you want then to take a region of the screen to zoom into, you want to do exactly the same: take the screen coordinates of the upper-left and lower-right region, translate them to the complex-plane coordinates, and make those the new uleft and lright variables. ie to zoom in on the box delimited by on-screen coordinates (x1,y1)..(x2,y2), use:
new_uleft = (uleft[0] + (x1/size[0]) * (xwidth), uleft[1] - (y1/size[1]) * (ywidth))
new_lright = (uleft[0] + (x2/size[0]) * (xwidth), uleft[1] - (y2/size[1]) * (ywidth))
(Obviously you'll need to recalculate the size, xwidth, ywidth and other dependent variables based on the new coordinates)
In case you're curious, the maths behind the mandelbrot set isn't that complicated (just complex).
All it is doing is taking a particular coordinate, treating it as a complex number, and then repeatedly squaring it and adding the original number to it.
For some numbers, doing this will cause the result diverge, constantly growing towards infinity as you repeat the process. For others, it will always stay below a certain level (eg. obviously (0.0, 0.0) never gets any bigger under this process. The mandelbrot set (the black region) is those coordinates which don't diverge. Its been shown that if any number gets above the square root of 5, it will diverge - your code is just using 2.0 as its approximation to sqrt(5) (~2.236), but this won't make much noticeable difference.
Usually the regions that diverge get plotted with the number of iterations of the process that it takes for them to exceed this value (the trials variable in your code) which is what produces the coloured regions.

Categories

Resources