Colors not varying - python graphics - python

I'm trying to reproduce something to this effect.
import graphics
from graphics import color_rgb
import random
window= graphics.GraphWin("x", 600, 400)
stripes = input("How many stripes should be on the flag")
stripes = int(stripes)
count = 0
count = int(count)
P1=graphics.Point(0,0) #left corner - anchor point
for x in range(stripes): #loop for number of stripes
col= random.randint(1,255)
stepdim = 400/stripes #size of divisions
stepdim = int(stepdim)
shrink = count*stepdim
shrink = int(shrink)
stepdim = stepdim*10 #enlarge to an increment below the last
stepdim = stepdim-shrink
stepdim = int(stepdim)
P2=graphics.Point(600,stepdim) #bottom right corner - ever shrinking
outsiderec=graphics.Rectangle(P1,P2) #
outsiderec.setFill(color_rgb(100, col, 0))
outsiderec.draw(window)
count= count + 1
count= int(count)
window.getMouse()
window.close()
I'm instead receiving one flat color.
I assume the problem is in my rand(int). I don't really know the ins and outs of it. Is it not running more than once?

Using your code as base I have tried to reproduce the expected result.
import graphics
from graphics import color_rgb
import random
window= graphics.GraphWin("x", 600, 400)
stripes = input("How many stripes should be on the flag")
stripes = int(stripes)
#count = 0
#count = int(count)
#P1=graphics.Point(0,0) #left corner - anchor point
stepdim = 400/stripes #size of divisions
for x in range(stripes): #loop for number of stripes
#col= random.randint(1,255)
#stepdim = int(stepdim)
#shrink = count*stepdim
#shrink = int(shrink)
#stepdim = stepdim*10 #enlarge to an increment below the last
#stepdim = stepdim-shrink
#stepdim = int(stepdim)
#P2=graphics.Point(600,stepdim) #bottom right corner - ever shrinking
P1=graphics.Point(0, stepdim * x) #left corner - anchor point
P2=graphics.Point(600,stepdim * (x + 1)) #bottom right corner - ever shrinking
outsiderec=graphics.Rectangle(P1,P2)
#outsiderec.setFill(color_rgb(100, col, 0))
red = random.randint(1, 255)
green = random.randint(1, 255)
blue = random.randint(1, 255)
outsiderec.setFill(color_rgb(red, green, blue))
outsiderec.draw(window)
#count= count + 1
#count= int(count)
window.getMouse()
window.close()
I have:
commented out all those statement that aren't needed
moved stepdim = 400/stripes from inside the loop to outside because you don't need to compute the same value for every loop
moved the P1=graphics.Point(0,0) inside the loop with a slight modification just to point to the top left corner of the stripe
modified the P2=graphics.Point(600,stepdim) to point to the bottom right corner of the stripe
added a random calculation for the three color components red = random.randint(1, 255), green = random.randint(1, 255), and blue = random.randint(1, 255)
Observations:
I have changed a little bit the way the stripes are drawn. Instead of drawing a shrunk version for every loop, the modified version just draws a fixed size stripe in consecutive positions.

Related

Pygame doesn't draw rectangles on the screen after a specific threshold

I'm trying to make a visualisation for various sorting algorithms and I was playing around with Pygame to try and draw the rectangles that I need.
In the below code, the user is given multiples inputs: the lowest value of the list to be sorted, the highest value, and the number of elements the list is going to have. The elements are going to be randomly generated.
Then I'm getting the user's screen size so that I can have an appropriate window for the visualisation. Based on the visualisation window and the user's input, I'm setting up the width and height of the rectangles, so that each rectangle has the same width and that they are scaled based on the highest value.
Almost everything is nice and fine with this approach, but there's one thing that I can't figure out. It seems that setting the number of elements (n, in the code below) too high, the rectangles are not being drawn.
My asumption is that after a specific threshold, RECT_W, which is the width of the rectangles, becomes to small for Pygame to draw it.
What options do I have to solve it, except of having the number of elements smaller than a specific value?
import random
import pygame
import color_constants as colors
import ctypes
import copy
from pygame.locals import *
# Read data based on user's input
def readData():
listOfNumbers = []
data = dict()
print("Lowest Value: ")
numLow = int(input())
print("Highest Value: ")
numHigh = int(input())
print("Length of list: ")
n = int(input())
for i in range(0, n):
listOfNumbers.append(random.randint(numLow, numHigh))
origLst = copy.copy(listOfNumbers)
data.update({'lst': origLst})
data.update({'numLow': numLow})
data.update({'numHigh': numHigh})
data.update({'n': n})
data.update({'sorted': listOfNumbers})
return data
if __name__ == "__main__":
data = readData()
# Getting the user's screen size
user32 = ctypes.windll.user32
SCREENSIZE = user32.GetSystemMetrics(0)-100, user32.GetSystemMetrics(1)-100
SCREEN_W = SCREENSIZE[0]
SCREEN_H = SCREENSIZE[1]
# Setting and scaling the size of rectangle based on the number of elements (n)
# and the highest number (numHigh)
RECT_W = SCREEN_W // data['n']
RECT_H = SCREEN_H / (data['numHigh'])
# Setting up the color literals
RED = (255, 255, 255)
GRAY = (0, 0, 0)
pygame.init()
screen = pygame.display.set_mode(SCREENSIZE)
running = True
while running:
for event in pygame.event.get():
if event.type == QUIT:
running = False
screen.fill(GRAY)
for i in range(data['n']):
rect = Rect(i*RECT_W, 0, RECT_W, RECT_H * data['lst'][i])
rect.bottom = SCREEN_H
pygame.draw.rect(screen, RED, rect)
pygame.display.flip()
If data['n'] is greater than SCREEN_W, RECT_W is 0. A coordinate is truncated when drawing. You cannot draw a fraction of a pixel. The size of the rectangle can only be integral (0, 1, 2 ...). Hence, you cannot draw a rectangle with a size less than 1.
You can draw the rectangles on a large surface and scale down the surface. However, the rectangles get blurred. So, this is no good option.

Pygame: How do I blit and rotate an image to connect two points on the screen?

Here is a test program. I started with two random dots and the line connecting them. Now, I want to take a given image (with x,y dimensions of 79 x 1080) and blit it on top of the guide line. I understand that arctan will give me the angle between the points on a cartesian grid, but because y is backwards the screen (x,y), I have to invert some values. I'm confused about the negating step.
If you run this repeatedly, you'll see the image is always parallel to the line, and sometimes on top, but not consistently.
import math
import pygame
import random
pygame.init()
screen = pygame.display.set_mode((600,600))
#target = (126, 270)
#start = (234, 54)
target = (random.randrange(600), random.randrange(600))
start = (random.randrange(600), random.randrange(600))
BLACK = (0,0,0)
BLUE = (0,0,128)
GREEN = (0,128,0)
pygame.draw.circle(screen, GREEN, start, 15)
pygame.draw.circle(screen, BLUE, target, 15)
pygame.draw.line(screen, BLUE, start, target, 5)
route = pygame.Surface((79,1080))
route.set_colorkey(BLACK)
BMP = pygame.image.load('art/trade_route00.png').convert()
(bx, by, bwidth, bheight) = route.get_rect()
route.blit(BMP, (0,0), area=route.get_rect())
# get distance within screen in pixels
dist = math.sqrt((start[0] - target[0])**2 + (start[1] - target[1])**2)
# scale to fit: use distance between points, and make width extra skinny.
route = pygame.transform.scale(route, (int(bwidth * dist/bwidth * 0.05), int( bheight * dist/bheight)))
# and rotate... (invert, as negative is for clockwise)
angle = math.degrees(math.atan2(-1*(target[1]-start[1]), target[0]-start[0]))
route = pygame.transform.rotate(route, angle + 90 )
position = route.get_rect()
HERE = (abs(target[0] - position[2]), target[1]) # - position[3]/2)
print(HERE)
screen.blit(route, HERE)
pygame.display.update()
print(start, target, dist, angle, position)
The main problem
The error is not due to the inverse y coordinates (0 at top, max at bottom) while rotating as you seems to think. That part is correct. The error is here:
HERE = (abs(target[0] - position[2]), target[1]) # - position[3]/2)
HERE must be the coordinates of the top-left corner of the rectangle inscribing your green and blue dots connected by the blue line. At those coordinates, you need to place the Surface route after rescaling.
You can get this vertex by doing:
HERE = (min(start[0], target[0]), min(start[1], target[1]))
This should solve the problem, and your colored dots should lay on the blue line.
A side note
Another thing you might wish to fix is the scaling parameter of route:
route = pygame.transform.scale(route, (int(bwidth * dist/bwidth * 0.05), int( bheight * dist/bheight)))
If my guess is correct and you want to preserve the original widht/height ratio in the rescaled route (since your original image is not a square) this should be:
route = pygame.transform.scale(route, (int(dist* bwidth/bheight), int(dist)))
assuming that you want height (the greater size in the original) be scaled to dist. So you may not need the 0.05, or maybe you can use a different shrinking parameter (probably 0.05 will shrink it too much).

Python turtle color changing and fill

This code draws a shape when the user inputs the number of squares to draw and the size of those squares. I want the color to change for every square that is drawn, and for it to get filled in. But the code gets stuck:
import turtle
t = turtle.Turtle()
size = int(input("How long do you want the side lengths to be?"))
number = int(input("How many squares do you want in the image?"))
red = 40.0
blue = 30.0
green = 10.0
def square (size):
count = 0
while count < 4:
t.forward(size)
t.right(90)
count = count + 1
def drawing(number):
times = 0
while times < number:
t.pencolor(red, blue, green)
t.fillcolor(red, blue, green)
t.begin_fill()
square(size)
t.right(360/number)
if t.filling():
t.pensize(5)
else:
t.pensize(3)
t.color()
(red + 1, blue + 1, green + 1)
times = times + 1
drawing(number)
There are several problems with your code -- the primary one is you're using the wrong color model. By default, turtle colors are specified as floating point values between 0.0 and 1.0. However, you want to use values like those in your code, we can switch this via colormode(255) which allows values from 0 to 255.
Next, a begin_fill() has to have a matching end_fill() which you are missing. Colors are specified in the order (red, green, blue), not (red, blue, green). Also, if you set both pencolor() and fillcolor() to the same color, you can simply use the one call color().
Finally, these two statements don't do anything:
t.color()
(red + 1, blue + 1, green + 1)
as far as your code is concerned. You need to rethink them. Below is my rework of your code which addresses the above issues and some other details:
from turtle import Screen, Turtle
def square(size):
count = 0
if turtle.filling():
turtle.pensize(5)
else:
turtle.pensize(3)
while count < 4:
turtle.forward(size)
turtle.right(90)
count += 1
def drawing(number):
red = 30
green = 10
blue = 20
times = 0
while times < number:
turtle.color(red % 255, green % 255, blue % 255)
turtle.begin_fill()
square(size)
turtle.end_fill()
turtle.right(360 / number)
red, green, blue = red + 20, green + 30, blue + 10
times += 1
size = int(input("How long do you want the side lengths to be? "))
number = int(input("How many squares do you want in the image? "))
screen = Screen()
screen.colormode(255)
turtle = Turtle()
turtle.speed('fastest') # because I have no patience
drawing(number)
screen.exitonclick()

cropping image by black pixel threshold

I have an image like this
In the upper part of the image there is some irrelevant space I want to crop. I marked it here:
The amount of black pixels in the first few lines is roughly the same.
So my idea is to loop through the rows of this image, starting from the top (row 0) and then check if row+10 has the same amount of black pixels.
If yes, go on, if no, this is the break point.
However, I cannot get this to work. Here is my code
for i in range(img.shape[0]):
low = sum(np.bincount(img[i,:])[0:5]) # number of black pixels
high = sum(np.bincount(img[i+10,:])[0:5]) #number of black pixels in row i+10
#print(i)
if(low-low*0.01 < high):
print(i)
break
then crop image:
imcrop = img[int(0+i):,:]
with np.bincount I sum the number of the five darkest pixels (0=black, 255=white)
and then loop until I find the break point.
By experimenting with the threshold levels, I found that it either outputs 0 or a number that is way too high.
What would be a better way to do this?
Quite slow but works. At first move along left to right to find boundary between black pixels and other pixels. After that move from right to left to find boundary between black and other pixels. Finally we get two list of boundary as left_data and right_data.
From first row check distance between left boundary to right boundary and move until distance is same then stop. Finally we get four corner of desired image.
import cv2
image = cv2.imread('sample.png') #Test Image
image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
height = image.shape[0]
width = image.shape[1]
Black = 0
left = 0
right = 0
data_left = [] #left boundary
data_right = [] #right boundary
for i in range(height):
for j in range(width-1):
first = image[i][j]
second = image[i][j+1]
if(first==Black and second!=Black):
left=j
data_left.append(left)
for i in range(height):
j = width-1
found = 0
while(found==0 and j>=0):
first = image[i][j]
second = image[i][j-1]
if(first==Black and second != Black):
right = j
found = 1
j = j-1
data_right.append(right)
left_start = [0,data_left[0]]
right_start = [0,data_right[0]]
left_end = 0
right_end = 0
i = 0
found = 0
while(i<len(data_left) and found == 0):
if((data_left[i]==left_start[1] and data_right[i]==right_start[1])==False):
found = 1
left_end = [i,data_left[i]]
right_end = [i,data_right[i]]
i = i+1
width = data_right[0]-data_left[0]
height = left_end[0]
pos_y = 0
pos_x = data_left[0]
crop_image = image[pos_y:pos_y+height, pos_x:pos_x+width]
cv2.imwrite('result.jpg',crop_image) # result image
Original Image
Result Image
To crop an Image in openCV
crop_img = img[200:400, 100:300]
where 100 and 300 are the width and height of the image crop, 200 and 400 are the top left co-ordinates of the image crop.

Edge detection in Python

I am trying to write a program where a user enters a number, and it draws that many rectangles to the screen, however the triangles cannot overlap. I have had a problem with that last part, and I am looking for some help. I borrowed the edge detection methods from an Al Sweigart book, and the full program he wrote can be found here:
http://inventwithpython.com/chapter18.html
Here is the program I am working on:
http://pastebin.com/EQJVH6xr
import pygame, sys, random
from pygame.locals import *
def doRectsOverlap(rect1, rect2):
for a, b in [(rect1, rect2)]:
# Check if a's corners are inside b
if ((isPointInsideRect(a.left, a.top, b)) or
(isPointInsideRect(a.left, a.bottom, b)) or
(isPointInsideRect(a.right, a.top, b)) or
(isPointInsideRect(a.right, a.bottom, b))):
return True
return False
def isPointInsideRect(x, y, rect):
if (x > rect.left) and (x < rect.right) and (y > rect.top) and (y < rect.bottom):
return True
else:
return False
# set up pygame
pygame.init()
# set up the window
WINDOWWIDTH = 600
WINDOWHEIGHT = 600
windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT), 0, 32)
pygame.display.set_caption('Rectangles')
# set up the colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
from random import choice
foo = [BLACK, RED, GREEN, BLUE]
# draw the background
windowSurface.fill(WHITE)
print('Please enter a number:')
number = input()
x = 0
array = []
for i in array:
while int(number) > x:
x = x+1
x1 = random.randint(1, 400)
y1 = random.randint(1, 400)
x2 = random.randint(1, 400)
y2 = random.randint(1, 400)
x3 = random.randint(1, 400)
y3 = random.randint(1, 400)
x4 = random.randint(1, 400)
y4 = random.randint(1, 400)
box = pygame.draw.rect(windowSurface,random.choice(foo), (x1, y1, x2, y2))
if doRectsOverlap(box, box) == False:
box
else:
x = x-1
# draw the window onto the screen
pygame.display.update()
Any help would be greatly appreciated. Thank you!
Well as a general answer, you are going to have to plot four co ordinance for each rectangle.
There are a few ways you can do this:
1) Just have the rectangle randomly placed and test if any of the new rectangle's points are inside of any of the existing rectangles. If they are, just keep generating until they are not. This will be very slow and inefficient though.
2) You can number crunch by restricting all of your possible random's to only available positions. This is can be done a variety of ways, it will be semi slow and probably fairly difficult to implement.
3) You can have the rectangles generate like in option 1, but in the case that the co-ordinance of the 4 points overlap, you can push the points off. To do this you simply have to set the violating co-ordinance to the co-ordinance of one of the corners and then add say, (5,5) or subtract or whatever. If you don't want to skew the rectangles too much you can regenerate the violating rectangle based on the modified point, or push all of the points an equivalent distance as the violating point.
I think option 3 is probably the best unless you are very strict on your random principles.
If you want me to clarify any of the above options let me know specifically what you want me to explain and I will do so. I can not however explain all of the possibilities for each option because it would take to long and way too many lines.
Cheers

Categories

Resources