I'm working in OpenCV (camera calibration and then creating 3d model) and till now I always printed a checkerboard pattern on paper and then took pictures needed for calibration. I tried to find a way to draw the pattern on the full screen with pre-defined square sizes (so I could set that square size in the calibration process), but I only found the Python turtle module which seems to only be for drawing on part of screen, and it always draws an arrow on last square. I need to draw the pattern with some small offset from the screen borders and, inside those offsets, draw a checkerboard with uniform squares. Also, I saw some people are drawing patterns in GIMP, but not on the full screen.
OpenCV has the function drawChessboardCorners but it requires founded corners from previous imported images, which need to be calibrated, so I think it doesn't make sense.
If anybody has an idea how to solve this problem, either with some program or module in some programming language (Python if possible), I would be grateful.
Here is a simple code for generating the chessboard pattern. However, the diameter of the chessboard is in pixel unit.
import numpy as np
h = 6
w = 8
size = 100
checkerboard = 255.0 * np.kron([[1, 0] * (w//2), [0, 1] * (w//2)] * (h//2), np.ones((size, size)))
I only found turtle module which seems to be only for drawing on part
of screen and it always draws arrow on last square.
Let's dispense with these two issues by drawing a grid in a window the size of the screen with no arrow on the last square:
from turtle import Screen, Turtle
BLOCK_SIZE = 72 # pixels
CURSOR_SIZE = 20 # pixels
BORDER = 1 # blocks
screen = Screen()
screen.setup(1.0, 1.0) # display size window
width, height = screen.window_width(), screen.window_height()
screen.setworldcoordinates(0, 0, width, height)
block = Turtle('square', visible=False) # hide the cursor completely
block.pencolor('black')
block.shapesize(BLOCK_SIZE / CURSOR_SIZE)
block.penup()
x_count = width // BLOCK_SIZE - BORDER * 2
x_width = x_count * BLOCK_SIZE
x_start = (width - x_width) // 2
x_limit = x_width + (BORDER + 1) * BLOCK_SIZE
y_count = height // BLOCK_SIZE - BORDER * 2
y_height = y_count * BLOCK_SIZE
y_start = (height - y_height) // 2
y_limit = y_height + (BORDER + 1) * BLOCK_SIZE
screen.tracer(False)
for parity_y, y in enumerate(range(y_start, y_limit, BLOCK_SIZE)):
block.sety(y)
for parity_x, x in enumerate(range(x_start, x_limit, BLOCK_SIZE)):
block.fillcolor(['white', 'black'][(parity_y % 2) == (parity_x % 2)])
block.setx(x)
block.stamp()
screen.tracer(True)
screen.mainloop()
(Hide your dock in OS X if you want to cover even more of the screen.)
Unfortunately, this is drawing in pixel units which is arbitrary. See my answer about drawing in a standardized measure using the pixel pitch value of your display.
Prepare your chessboard pattern in your favourite graphics editor, save the file onto the computer you want to use to display it for your calibration, then just display it when needed. I think you might be over-thinking the problem...
Related
I'm writing a python game.
There is a certain map in it (a two-dimensional array storing cells) and I want to make it possible to place blocks on them.
I have:
A two-dimensional array of cells.
the size of one cell.
the position of the mouse on the screen.
camera position.
Camera zoom (for example, zoom = 1 is the normal size and 1.5 is a slightly enlarged camera).
I want to arrange the blocks in cells by changing the size of the camera.
I have already done a search for mouse coordinates on the map in cells, but it not work when I zoom the camera
Here's what I wrote:
cell_size = 64
zoom = 1
camera_pos = getcamerapos()
mouse_pos = getmousepos()
def GetMouseCellPos(self):
MapPosX = int(-camera_pos[0]) # Map position on the screen
MapPosY = int(-camera_pos[1]) # Map position on the screen
MouseMapPosX = -int(MapPosX - mouse_pos[0]) # Mouse position on the map
MouseMapPosY = -int(MapPosY - mouse_pos[1]) # Mouse position on the map
MouseCellPosX = int(MouseMapPosX // cell_size) # Mouse position on the map in blocks
MouseCellPosY = int(MouseMapPosY // cell_size) # Mouse position on the map in blocks
return MouseCellPosX, MouseCellPosY
I need to somehow adapt this code to the camera zoom
To make everything work fine
I was able to solve my problem:
def GetMouseCellMap(self, mousepos: (int, int), camerapos: (int, int), camerasize, zoom):
MouseCellPosX = int(-(-camerapos[0] - (camerasize[0] / 2) - -((camerasize[0] / 2) - mousepos[0]) * zoom) // self.cellsize)
MouseCellPosY = int(-(-camerapos[1] - (camerasize[1] / 2) - -((camerasize[1] / 2) - mousepos[1]) * zoom) // self.cellsize)
return MouseCellPosX, MouseCellPosY
Using PyCairo, I want to be able to have a method that can put, resize & rotate a given ImageSurface on a context, but rotating by the center of the image (not the top-left)
Okay I've tried the examples I've found here but without any success.
Let's introduce the "context" in details.
I've got a "finale" ImageSurface (say A) on which some other images & texts are written.
I want to put on it another ImageSurface (say B), at a specified position where this position is the top-left where to put B on A. Then I need to resize B (reduce its size) and to rotate it by its center instead of by its top-left corner.
Here is an illustration of the wanted result :
I've tried the following but without success:
def draw_rotated_image(ctx, image_surface, left, top, width, height, angle):
ctx.save()
w = image_surface.get_width()
h = image_surface.get_height()
cl = left / (width/w)
ct = top / (height/h)
ctx.rotate(angle*3.1415927/180)
ctx.scale(width/w, height/h)
ctx.translate(cl + (-0.5*w),ct + (-0.5*h) )
ctx.set_source_surface(image_surface, 0, 0)
ctx.paint()
ctx.restore()
return
Thanks a lot for your help :)
Well, I finally made it ! (thanks to my 14 year old son who made me revise my trigonometry)
I'm trying to explain here my solution.
First, I AM NOT A MATHEMATICIAN. So there is probably a best way, and surely my explanation have errors, but I'm just explaining the logical way I have used to get the good result.
The best idea for that is to first draw a circle around the rectangle, because we need to move the top-left corner of this rectangle, around its own circle, according to the desired angle.
So to get the radius of the rectangle circle, we need to compute its hypothenuse, then to divide by 2 :
hypothenuse = math.hypot(layerWidth,layerHeight)
radius = hypothenuse / 2
Then we will be able to draw a circle around the rectangle.
Second, we need to know at which angle, on this circle, is the actual top-left corner of the rectangle.
So for that, we need compute the invert tangent of the rectangle, which is arc-tan(height/width).
But because we want to know how many degrees are we far from 0°, we need to compute the opposite so arc-tan(width/height).
Finally, another singularity is that Cairo 0° is in fact at 90°, so we will have to rotate again.
This can be shown by this simple graphic :
So finally, what is necessary to understand ?
If you want to draw a layer, with an angle, rotated by its center, the top-left point will move around the circle according to the desired angle.
The top-left position with a given angle of 0 needs to be "the reference".
So we need to get the new X-Y position where to start putting the layer to be able to rotate it :
Now, we can write a function that will return the X-Y pos of the top left rectangle where to draw it with a given angle :
def getTopLeftForRectangleAtAngle(layerLeft,layerTop,layerWidth,layerHeight,angleInDegrees):
# now we need to know the angle of the top-left corner
# for that, we need to compute the arc tangent of the triangle-rectangle:
layerAngleRad = math.atan((layerWidth / layerHeight))
layerAngle = math.degrees(layerAngleRad)
# 0° is 3 o'clock. So we need to rotate left to 90° first
# Then we want that 0° will be the top left corner which is "layerAngle" far from 0
if (angleInDegrees >= (90 + layerAngle)):
angleInDegrees -= (90 + layerAngle)
else:
angleInDegrees = 360 - ((90 + layerAngle) - angleInDegrees)
angle = (angleInDegrees * math.pi / 180.0)
centerLeft = layerLeft + (layerWidth / 2)
centerTop = layerTop + (layerHeight / 2)
# hypothenuse will help us knowing the circle radius
hypothenuse = math.hypot(layerWidth,layerHeight)
radius = hypothenuse / 2
pointX = centerLeft + radius * math.cos(angle)
pointY = centerTop + radius * math.sin(angle)
return (pointX,pointY)
And finally, here is how to use it with an image we want to resize, rotate and write on a context:
def draw_rotated_image(ctx, image_surface, left, top, width, height, angle=0.0, alpha=1.0):
ctx.save()
w = image_surface.get_width()
h = image_surface.get_height()
# get the new top-left position according to the given angle
newTopLeft = getTopLeftForRectangleAtAngle(left, top, width, height, angle)
# translate
ctx.translate(newTopLeft[0], newTopLeft[1])
# rotate
ctx.rotate(angle * math.pi / 180)
# scale & write
ctx.scale(width/w, height/h)
ctx.set_source_surface(image_surface, 0, 0)
ctx.paint_with_alpha(alpha)
ctx.restore()
return
So I'm still very new to python and trying to learn through making small projects.
The game I'm making is meant to test your mouse accuracy by creating a bunch of random circles which the player is meant to click in a given amount of time. At the end of the game, it should tell the player their score, and how many misclicks they had.
I've been using turtle to try and do this, but I'm stuck:
import turtle
import random
t = turtle.Pen()
win = turtle.Screen()
win.bgcolor("lightgreen")
win.title("clicky")
def mycircle(red, green, blue):
t.color(red, green, blue)
t.begin_fill()
x = random.randint(10,50)
t.circle(x)
t.end_fill()
t.up()
y = random.randint(0,360)
t.seth(y)
if t.xcor() < -300 or t.xcor() > 300:
t.goto(0, 0)
elif t.ycor() < -300 or t.ycor() > 300:
t.goto(0, 0)
z = random.randint(0,100)
t.forward(z)
t.down()
for i in range(0, 20):
a = random.randint(0,100)/100.0
b = random.randint(0,100)/100.0
c = random.randint(0,100)/100.0
mycircle(a, b, c)
The main issues I've been trying to figure out are:
How can I make the circles spawn further from each other? They overlap
quite often and I want that to be avoided.
How can I make the circles spawn instantly rather than having to be
drawn?
How can I make the circles spawn further from each other?
We can keep track of circles already created and make sure their centers are at least a diameter away from each other. Your current circle placement logic is too complicated along with being faulty. Let's try to simplify it and make sure circles are drawn completely within the window.
How can I make the circles spawn instantly rather than having to be
drawn?
We could stamp them rather than draw them. However, since you are drawing so few circles, we can make every circle a turtle. This makes determining if you clicked on a circle, and removing that circle, simpler. I've added code, for you to expand on, that removes any circle that you click on:
from turtle import Turtle, Screen
from random import random, randint
CURSOR_SIZE = 20
def my_circle(color):
radius = randint(10, 50)
circle = Turtle('circle', visible=False)
circle.shapesize(radius / CURSOR_SIZE)
circle.color(color)
circle.penup()
while True:
nx = randint(2 * radius - width // 2, width // 2 - radius * 2)
ny = randint(2 * radius - height // 2, height // 2 - radius * 2)
circle.goto(nx, ny)
for other_radius, other_circle in circles:
if circle.distance(other_circle) < 2 * max(radius, other_radius):
break # too close, try again
else: # no break
break
circle.showturtle()
circle.onclick(lambda x, y, t=circle: t.hideturtle()) # expand this into a complete function
return radius, circle
screen = Screen()
screen.bgcolor("lightgreen")
screen.title("clicky")
width, height = screen.window_width(), screen.window_height()
circles = []
for _ in range(0, 20):
rgb = (random(), random(), random())
circles.append(my_circle(rgb))
screen.mainloop()
One issue you need to work out is making sure your circle color isn't too similar to (or the same as) your background color, otherwise you'll be hunting an invisible circle. Also, we might be able to speed up the circle drawing process even more, if needed.
So i am making a game in python and pygame and i have the indow setup like this
display = pygame.display.set_mode((0,0), pygame.FULLSCREEN)
which makes the size of the window about 1334 X 800 so i based all the sprites and backgrounds on that size of screen but as you know not everyone has the same sized screen as me so my question is how can i make images scale with how big the monitor screen is
(P.S The game is in fullscreen mode)
First, how do you get the resolution and the scaling factor?
This is tricky, because someone's screen may not have the same aspect ratio as your 1334x800. You can letterbox (in various different ways) or stretch the sprites; you need to decide what you want, but I'll show one letterboxing possibility:
NOMINAL_WIDTH, NOMINAL_HEIGHT = 1334., 800.
surface = display.get_surface()
width, height = surface.get_width(), surface.get_height()
xscale = width / NOMINAL_WIDTH
yscale = height / NOMINAL_HEIGHT
if xscale < 1 and yscale < 1:
scale = max(xscale, yscale)
elif xscale > 1 and yscale > 1:
scale = min(xscale, yscale)
else:
scale = 1.0
Now, how do you scale each sprite and background?
Well, first, are you sure you want to? It may be simpler to just transform the whole surface. Whether this is slower or faster is hard to predict without testing (and probably not relevant anyway), but it will definitely look better (because any interpolation, dithering, antialiasing, etc. happens after compositing, instead of before—unless you're going for that 8-bit look, of course, in which case it will destroy the look…). You can do this by compositing everything to an off-screen surface of 1334x800 (or, better, scaling everything up by a constant factor), then transforming that surface for display. (Note that the transform methods include an optional DestSurface argument. You can use this to directly transform from the offscreen surface to the display's surface.)
But let's assume you want to do it the way you asked.
You can do this when loading the sprites. For example:
def rescale(surf, scale):
new_width, new_height = surf.get_width() * scale, surf.get_height() * scale
return pygame.transform.smoothscale(surf, (new_width, new_height))
class ScaledSprite(pygame.sprite.Sprite):
def __init__(self, path, scale):
pygame.sprite.Sprite.__init__(self)
self.image = rescale(pygame.image.load(path), scale)
self.rect = self.image.get_rect()
And the same for the backgrounds.
from this SO question, you can get the size of the monitor with
infoObject = pygame.display.Info()
which gets the height and width of the screen as infoObject.current_w and infoObject.current_h
You can then use these values to scale everything appropriately.
Hey I'm trying to rotate a rectangle around its center and when I try to rotate the rectangle, it moves up and to the left at the same time. Does anyone have any ideas on how to fix this?
def rotatePoint(self, angle, point, origin):
sinT = sin(radians(angle))
cosT = cos(radians(angle))
return (origin[0] + (cosT * (point[0] - origin[0]) - sinT * (point[1] - origin[1])),
origin[1] + (sinT * (point[0] - origin[0]) + cosT * (point[1] - origin[1])))
def rotateRect(self, degrees):
center = (self.collideRect.centerx, self.collideRect.centery)
self.collideRect.topleft = self.rotatePoint(degrees, self.collideRect.topleft, center)
self.collideRect.topright = self.rotatePoint(degrees, self.collideRect.topright, center)
self.collideRect.bottomleft = self.rotatePoint(degrees, self.collideRect.bottomleft, center)
self.collideRect.bottomright = self.rotatePoint(degrees, self.collideRect.bottomright, center)
The rotation code looks to be fine - but, you are aware that pygame's internals don't work with rotated rectangles, do you?
Unless you have some code you wrote yourself with the new rectangle corners, what this does is to define a new rectangle, with sides parallel to the Surface edges, where the original rectangle, when rotated, could be inscribed to, not a rectangle at the same size than the original at a skewed angle. Any Pygame function to which you pass the "self.collideRect" object after the rotation will just do that: treat the rectangle as aligned to the surface,
just as if it has been created with the corners it has now.
If your code requires you to check for things, or even draw, inside a rotated rectangle, you have to perform all the calculations as they where prior to the rotation, and just perform the coordinate rotation at the time of displaying what you want. That is, you work with a global coordinate transform, that is applied in the last step of rendering.
Maybe this can help you:
#load image
image1 = pygame.image.load(file)
#get width and height of unrotated image
width1,height1 = image1.get_size()
#rotate image
image2 = pygame.transform.rotate(image1, angle)
#get width,height of rotated image
width2,height2 = image2.get_size()
#blit rotated image (positon - difference of width or height /2)
display.blit(image2,[round(x - (width1 - width2)/2),round(y - (height1 - height2)/2)])