How can I modify the code shown below to apply different depth level of quadtree to each instance contained in the image. I would like to apply a finer quadtree level on instances such as the 'teddy bear' and 'plant' while a coarse quadtree level on the 'background', 'table' and 'plant vase' instances.
EFFORTS UNDERTAKEN SO FAR (BIT LONG TO READ)
I have an image such that each x,y coordinate already has a value associated based on the instance as follows:
INSTANCE PIXEL VALUE
background 0.0
chair 1.0
table 2.0
teddy bear 3.0
plant 4.0
vase 5.0
This is what the image looks like when plotted as follows
img_arr = np.loadtxt("seg_mask_retrieved.txt")
mask_image = plt.imshow(img_arr, cmap='hot', interpolation='nearest')
I am able to perform instance value based quadtree (Results shown at the end). All I need now is to modify this code in order to add depth level based on each instance:
import numpy as np
import math
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def distanceToCenter(self, center):
return math.sqrt((center.x-self.x)**2 + (center.y-self.y)**2)
class Rectangle:
def __init__(self, center, width, height):
self.center = center
self.width = width
self.height = height
self.west = center.x - width
self.east = center.x + width
self.north = center.y - height
self.south = center.y + height
def containsPoint(self, point):
return (self.west <= point.x < self.east and
self.north <= point.y < self.south)
def intersects(self, range):
return not (range.west > self.east or
range.east < self.west or
range.north > self.south or
range.south < self.north)
def draw(self, ax, c='g', lw=1, **kwargs):
x1, y1 = self.west, self.north
x2, y2 = self.east, self.south
ax.plot([x1,x2,x2,x1,x1], [y1,y1,y2,y2,y1], c=c, lw=lw, **kwargs)
class QuadTree:
def __init__(self, boundary, capacity = 1):
self.boundary = boundary
self.capacity = capacity
self.points = []
self.instances = []
self.unique_instances = []
self.divided = False
def insert(self, point, instance):
# if the point is in the range of current quadTree
if not self.boundary.containsPoint(point):
return False
if self.divided:
if self.nw.insert(point, instance):
return True
elif self.ne.insert(point, instance):
return True
elif self.sw.insert(point, instance):
return True
elif self.se.insert(point, instance):
return True
else:
assert "Should never happen"
else:
# if has not reached capcaity
instance_already_seen = instance in self.unique_instances
if instance_already_seen or len(self.unique_instances) < self.capacity:
self.points.append(point)
self.instances.append(instance)
if not instance_already_seen:
self.unique_instances.append(instance)
return True
self.divide()
assert self.insert(point, instance)
return True
def queryRange(self, range):
found_points = []
if not self.boundary.intersects(range):
return []
if self.divided:
found_points.extend(self.nw.queryRange(range))
found_points.extend(self.ne.queryRange(range))
found_points.extend(self.sw.queryRange(range))
found_points.extend(self.se.queryRange(range))
else:
for point in self.points:
if range.containsPoint(point):
found_points.append(point)
return found_points
def queryRadius(self, range, center):
if not self.boundary.intersects(range):
return []
found_points = []
if self.divided:
found_points.extend(self.nw.queryRadius(range, center))
found_points.extend(self.ne.queryRadius(range, center))
found_points.extend(self.sw.queryRadius(range, center))
found_points.extend(self.se.queryRadius(range, center))
else:
for point in self.points:
if range.containsPoint(point) and point.distanceToCenter(center) <= range.width:
found_points.append(point)
return found_points
def divide(self):
center_x = self.boundary.center.x
center_y = self.boundary.center.y
new_width = self.boundary.width / 2
new_height = self.boundary.height / 2
nw = Rectangle(Point(center_x - new_width, center_y - new_height), new_width, new_height)
self.nw = QuadTree(nw)
ne = Rectangle(Point(center_x + new_width, center_y - new_height), new_width, new_height)
self.ne = QuadTree(ne)
sw = Rectangle(Point(center_x - new_width, center_y + new_height), new_width, new_height)
self.sw = QuadTree(sw)
se = Rectangle(Point(center_x + new_width, center_y + new_height), new_width, new_height)
self.se = QuadTree(se)
self.divided = True
self.unique_instances = []
for (point, instance) in zip(self.points, self.instances):
assert self.insert(point, instance)
self.points = []
self.instances = []
def __len__(self):
if self.divided:
return len(self.nw) + len(self.ne) + len(self.sw) + len(self.se)
else:
return len(self.points)
def draw(self, ax):
if self.divided:
self.nw.draw(ax)
self.ne.draw(ax)
self.se.draw(ax)
self.sw.draw(ax)
else:
self.boundary.draw(ax)
The code is run using the following commands
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec
from quadtree import Point, Rectangle, QuadTree
DPI = 72
INPUT_FILE = 'seg_mask_retrieved.npy'
mask_image = np.load(INPUT_FILE)
height, width = mask_image.shape
domain = Rectangle(Point(width/2, height/2), width/2, height/2)
qtree = QuadTree(domain)
for y in range(height):
for x in range(width):
assert qtree.insert(Point(x, y), mask_image[y, x])
print('Total points: ', len(qtree))
# draw rectangles
fig = plt.figure(figsize=(700/DPI, 500/DPI), dpi=DPI)
ax = plt.subplot()
ax.imshow(mask_image, cmap='hot', interpolation='nearest')
ax.set_xlim(0, width)
ax.set_ylim(0, height)
qtree.draw(ax)
ax.invert_yaxis()
plt.tight_layout()
plt.savefig('search-quadtree.png', DPI=72)
plt.show()
The output looks like this
After adding depth to each instance, the output should somewhat look like this (only the chair instance has been modified to show the output just for demonstration of a coarse quadtree)
Related
I'm running a code from github site and it has this error ( last lines) :
File "D:\Anaconda3\lib\site-packages\pyqtgraph\opengl\GLViewWidget.py", line 152, in viewMatrix
tr.translate(-center.x(), -center.y(), -center.z())
AttributeError: 'int' object has no attribute 'x'
I found that the error is related to pyqtgraph libraries and i didn't change their files
just install the last versions of PyOpenGl and PyQtGraph in spyder
can you please help me in this error?
We need more information,
The problem seems to be related to the fact that center is an int but you use it as an object with attributs
Can we see center's initialization ?
the GlViewWidget.py file :
from ..Qt import QtCore, QtGui, QtOpenGL, QT_LIB
from OpenGL.GL import *
import OpenGL.GL.framebufferobjects as glfbo
import numpy as np
from .. import Vector
from .. import functions as fn
##Vector = QtGui.QVector3D
ShareWidget = None
class GLViewWidget(QtOpenGL.QGLWidget):
"""
Basic widget for displaying 3D data
- Rotation/scale controls
- Axis/grid display
- Export options
High-DPI displays: Qt5 should automatically detect the correct resolution.
For Qt4, specify the ``devicePixelRatio`` argument when initializing the
widget (usually this value is 1-2).
"""
def __init__(self, parent=None, devicePixelRatio=None):
global ShareWidget
if ShareWidget is None:
## create a dummy widget to allow sharing objects (textures, shaders, etc) between views
ShareWidget = QtOpenGL.QGLWidget()
QtOpenGL.QGLWidget.__init__(self, parent, ShareWidget)
self.setFocusPolicy(QtCore.Qt.ClickFocus)
self.opts = {
'center': Vector(0,0,0), ## will always appear at the center of the widget
'distance': 10.0, ## distance of camera from center
'fov': 60, ## horizontal field of view in degrees
'elevation': 30, ## camera's angle of elevation in degrees
'azimuth': 45, ## camera's azimuthal angle in degrees
## (rotation around z-axis 0 points along x-axis)
'viewport': None, ## glViewport params; None == whole widget
'devicePixelRatio': devicePixelRatio,
}
self.setBackgroundColor('k')
self.items = []
self.noRepeatKeys = [QtCore.Qt.Key_Right, QtCore.Qt.Key_Left, QtCore.Qt.Key_Up, QtCore.Qt.Key_Down, QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown]
self.keysPressed = {}
self.keyTimer = QtCore.QTimer()
self.keyTimer.timeout.connect(self.evalKeyState)
self.makeCurrent()
def addItem(self, item):
self.items.append(item)
if hasattr(item, 'initializeGL'):
self.makeCurrent()
try:
item.initializeGL()
except:
self.checkOpenGLVersion('Error while adding item %s to GLViewWidget.' % str(item))
item._setView(self)
#print "set view", item, self, item.view()
self.update()
def removeItem(self, item):
self.items.remove(item)
item._setView(None)
self.update()
def initializeGL(self):
self.resizeGL(self.width(), self.height())
def setBackgroundColor(self, *args, **kwds):
"""
Set the background color of the widget. Accepts the same arguments as
pg.mkColor() and pg.glColor().
"""
self.opts['bgcolor'] = fn.glColor(*args, **kwds)
self.update()
def getViewport(self):
vp = self.opts['viewport']
dpr = self.devicePixelRatio()
if vp is None:
return (0, 0, int(self.width() * dpr), int(self.height() * dpr))
else:
return tuple([int(x * dpr) for x in vp])
def devicePixelRatio(self):
dpr = self.opts['devicePixelRatio']
if dpr is not None:
return dpr
if hasattr(QtOpenGL.QGLWidget, 'devicePixelRatio'):
return QtOpenGL.QGLWidget.devicePixelRatio(self)
else:
return 1.0
def resizeGL(self, w, h):
pass
#glViewport(*self.getViewport())
#self.update()
def setProjection(self, region=None):
m = self.projectionMatrix(region)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
a = np.array(m.copyDataTo()).reshape((4,4))
glMultMatrixf(a.transpose())
def projectionMatrix(self, region=None):
if region is None:
dpr = self.devicePixelRatio()
region = (0, 0, self.width() * dpr, self.height() * dpr)
x0, y0, w, h = self.getViewport()
dist = self.opts['distance']
fov = self.opts['fov']
nearClip = dist * 0.001
farClip = dist * 1000.
r = nearClip * np.tan(fov * 0.5 * np.pi / 180.)
t = r * h / w
## Note that X0 and width in these equations must be the values used in viewport
left = r * ((region[0]-x0) * (2.0/w) - 1)
right = r * ((region[0]+region[2]-x0) * (2.0/w) - 1)
bottom = t * ((region[1]-y0) * (2.0/h) - 1)
top = t * ((region[1]+region[3]-y0) * (2.0/h) - 1)
tr = QtGui.QMatrix4x4()
tr.frustum(left, right, bottom, top, nearClip, farClip)
return tr
def setModelview(self):
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
m = self.viewMatrix()
a = np.array(m.copyDataTo()).reshape((4,4))
glMultMatrixf(a.transpose())
def viewMatrix(self):
tr = QtGui.QMatrix4x4()
tr.translate( 0.0, 0.0, -self.opts['distance'])
tr.rotate(self.opts['elevation']-90, 1, 0, 0)
tr.rotate(self.opts['azimuth']+90, 0, 0, -1)
center = self.opts['center']
tr.translate(-center.x(), -center.y(), -center.z())
return tr
def itemsAt(self, region=None):
"""
Return a list of the items displayed in the region (x, y, w, h)
relative to the widget.
"""
region = (region[0], self.height()-(region[1]+region[3]), region[2], region[3])
#buf = np.zeros(100000, dtype=np.uint)
buf = glSelectBuffer(100000)
try:
glRenderMode(GL_SELECT)
glInitNames()
glPushName(0)
self._itemNames = {}
self.paintGL(region=region, useItemNames=True)
finally:
hits = glRenderMode(GL_RENDER)
items = [(h.near, h.names[0]) for h in hits]
items.sort(key=lambda i: i[0])
return [self._itemNames[i[1]] for i in items]
def paintGL(self, region=None, viewport=None, useItemNames=False):
"""
viewport specifies the arguments to glViewport. If None, then we use self.opts['viewport']
region specifies the sub-region of self.opts['viewport'] that should be rendered.
Note that we may use viewport != self.opts['viewport'] when exporting.
"""
if viewport is None:
glViewport(*self.getViewport())
else:
glViewport(*viewport)
self.setProjection(region=region)
self.setModelview()
bgcolor = self.opts['bgcolor']
glClearColor(*bgcolor)
glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT )
self.drawItemTree(useItemNames=useItemNames)
def drawItemTree(self, item=None, useItemNames=False):
if item is None:
items = [x for x in self.items if x.parentItem() is None]
else:
items = item.childItems()
items.append(item)
items.sort(key=lambda a: a.depthValue())
for i in items:
if not i.visible():
continue
if i is item:
try:
glPushAttrib(GL_ALL_ATTRIB_BITS)
if useItemNames:
glLoadName(i._id)
self._itemNames[i._id] = i
i.paint()
except:
from .. import debug
debug.printExc()
msg = "Error while drawing item %s." % str(item)
ver = glGetString(GL_VERSION)
if ver is not None:
ver = ver.split()[0]
if int(ver.split(b'.')[0]) < 2:
print(msg + " The original exception is printed above; however, pyqtgraph requires OpenGL version 2.0 or greater for many of its 3D features and your OpenGL version is %s. Installing updated display drivers may resolve this issue." % ver)
else:
print(msg)
finally:
glPopAttrib()
else:
glMatrixMode(GL_MODELVIEW)
glPushMatrix()
try:
tr = i.transform()
a = np.array(tr.copyDataTo()).reshape((4,4))
glMultMatrixf(a.transpose())
self.drawItemTree(i, useItemNames=useItemNames)
finally:
glMatrixMode(GL_MODELVIEW)
glPopMatrix()
def setCameraPosition(self, pos=None, distance=None, elevation=None, azimuth=None):
if pos is not None:
self.opts['center'] = pos
if distance is not None:
self.opts['distance'] = distance
if elevation is not None:
self.opts['elevation'] = elevation
if azimuth is not None:
self.opts['azimuth'] = azimuth
self.update()
def cameraPosition(self):
"""Return current position of camera based on center, dist, elevation, and azimuth"""
center = self.opts['center']
dist = self.opts['distance']
elev = self.opts['elevation'] * np.pi/180.
azim = self.opts['azimuth'] * np.pi/180.
pos = Vector(
center.x() + dist * np.cos(elev) * np.cos(azim),
center.y() + dist * np.cos(elev) * np.sin(azim),
center.z() + dist * np.sin(elev)
)
return pos
def orbit(self, azim, elev):
"""Orbits the camera around the center position. *azim* and *elev* are given in degrees."""
self.opts['azimuth'] += azim
self.opts['elevation'] = np.clip(self.opts['elevation'] + elev, -90, 90)
self.update()
def pan(self, dx, dy, dz, relative='global'):
"""
Moves the center (look-at) position while holding the camera in place.
============== =======================================================
**Arguments:**
*dx* Distance to pan in x direction
*dy* Distance to pan in y direction
*dx* Distance to pan in z direction
*relative* String that determines the direction of dx,dy,dz.
If "global", then the global coordinate system is used.
If "view", then the z axis is aligned with the view
direction, and x and y axes are inthe plane of the
view: +x points right, +y points up.
If "view-upright", then x is in the global xy plane and
points to the right side of the view, y is in the
global xy plane and orthogonal to x, and z points in
the global z direction.
============== =======================================================
Distances are scaled roughly such that a value of 1.0 moves
by one pixel on screen.
Prior to version 0.11, *relative* was expected to be either True (x-aligned) or
False (global). These values are deprecated but still recognized.
"""
# for backward compatibility:
relative = {True: "view-upright", False: "global"}.get(relative, relative)
if relative == 'global':
self.opts['center'] += QtGui.QVector3D(dx, dy, dz)
elif relative == 'view-upright':
cPos = self.cameraPosition()
cVec = self.opts['center'] - cPos
dist = cVec.length() ## distance from camera to center
xDist = dist * 2. * np.tan(0.5 * self.opts['fov'] * np.pi / 180.) ## approx. width of view at distance of center point
xScale = xDist / self.width()
zVec = QtGui.QVector3D(0,0,1)
xVec = QtGui.QVector3D.crossProduct(zVec, cVec).normalized()
yVec = QtGui.QVector3D.crossProduct(xVec, zVec).normalized()
self.opts['center'] = self.opts['center'] + xVec * xScale * dx + yVec * xScale * dy + zVec * xScale * dz
elif relative == 'view':
# pan in plane of camera
elev = np.radians(self.opts['elevation'])
azim = np.radians(self.opts['azimuth'])
fov = np.radians(self.opts['fov'])
dist = (self.opts['center'] - self.cameraPosition()).length()
fov_factor = np.tan(fov / 2) * 2
scale_factor = dist * fov_factor / self.width()
z = scale_factor * np.cos(elev) * dy
x = scale_factor * (np.sin(azim) * dx - np.sin(elev) * np.cos(azim) * dy)
y = scale_factor * (np.cos(azim) * dx + np.sin(elev) * np.sin(azim) * dy)
self.opts['center'] += QtGui.QVector3D(x, -y, z)
else:
raise ValueError("relative argument must be global, view, or view-upright")
self.update()
def pixelSize(self, pos):
"""
Return the approximate size of a screen pixel at the location pos
Pos may be a Vector or an (N,3) array of locations
"""
cam = self.cameraPosition()
if isinstance(pos, np.ndarray):
cam = np.array(cam).reshape((1,)*(pos.ndim-1)+(3,))
dist = ((pos-cam)**2).sum(axis=-1)**0.5
else:
dist = (pos-cam).length()
xDist = dist * 2. * np.tan(0.5 * self.opts['fov'] * np.pi / 180.)
return xDist / self.width()
def mousePressEvent(self, ev):
self.mousePos = ev.pos()
def mouseMoveEvent(self, ev):
diff = ev.pos() - self.mousePos
self.mousePos = ev.pos()
if ev.buttons() == QtCore.Qt.LeftButton:
if (ev.modifiers() & QtCore.Qt.ControlModifier):
self.pan(diff.x(), diff.y(), 0, relative='view')
else:
self.orbit(-diff.x(), diff.y())
elif ev.buttons() == QtCore.Qt.MidButton:
if (ev.modifiers() & QtCore.Qt.ControlModifier):
self.pan(diff.x(), 0, diff.y(), relative='view-upright')
else:
self.pan(diff.x(), diff.y(), 0, relative='view-upright')
def mouseReleaseEvent(self, ev):
pass
# Example item selection code:
#region = (ev.pos().x()-5, ev.pos().y()-5, 10, 10)
#print(self.itemsAt(region))
## debugging code: draw the picking region
#glViewport(*self.getViewport())
#glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT )
#region = (region[0], self.height()-(region[1]+region[3]), region[2], region[3])
#self.paintGL(region=region)
#self.swapBuffers()
def wheelEvent(self, ev):
delta = 0
if QT_LIB in ['PyQt4', 'PySide']:
delta = ev.delta()
else:
delta = ev.angleDelta().x()
if delta == 0:
delta = ev.angleDelta().y()
if (ev.modifiers() & QtCore.Qt.ControlModifier):
self.opts['fov'] *= 0.999**delta
else:
self.opts['distance'] *= 0.999**delta
self.update()
def keyPressEvent(self, ev):
if ev.key() in self.noRepeatKeys:
ev.accept()
if ev.isAutoRepeat():
return
self.keysPressed[ev.key()] = 1
self.evalKeyState()
def keyReleaseEvent(self, ev):
if ev.key() in self.noRepeatKeys:
ev.accept()
if ev.isAutoRepeat():
return
try:
del self.keysPressed[ev.key()]
except:
self.keysPressed = {}
self.evalKeyState()
def evalKeyState(self):
speed = 2.0
if len(self.keysPressed) > 0:
for key in self.keysPressed:
if key == QtCore.Qt.Key_Right:
self.orbit(azim=-speed, elev=0)
elif key == QtCore.Qt.Key_Left:
self.orbit(azim=speed, elev=0)
elif key == QtCore.Qt.Key_Up:
self.orbit(azim=0, elev=-speed)
elif key == QtCore.Qt.Key_Down:
self.orbit(azim=0, elev=speed)
elif key == QtCore.Qt.Key_PageUp:
pass
elif key == QtCore.Qt.Key_PageDown:
pass
self.keyTimer.start(16)
else:
self.keyTimer.stop()
def checkOpenGLVersion(self, msg):
## Only to be called from within exception handler.
ver = glGetString(GL_VERSION).split()[0]
if int(ver.split(b'.')[0]) < 2:
from .. import debug
debug.printExc()
raise Exception(msg + " The original exception is printed above; however, pyqtgraph requires OpenGL version 2.0 or greater for many of its 3D features and your OpenGL version is %s. Installing updated display drivers may resolve this issue." % ver)
else:
raise
def readQImage(self):
"""
Read the current buffer pixels out as a QImage.
"""
w = self.width()
h = self.height()
self.repaint()
pixels = np.empty((h, w, 4), dtype=np.ubyte)
pixels[:] = 128
pixels[...,0] = 50
pixels[...,3] = 255
glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels)
# swap B,R channels for Qt
tmp = pixels[...,0].copy()
pixels[...,0] = pixels[...,2]
pixels[...,2] = tmp
pixels = pixels[::-1] # flip vertical
img = fn.makeQImage(pixels, transpose=False)
return img
def renderToArray(self, size, format=GL_BGRA, type=GL_UNSIGNED_BYTE, textureSize=1024, padding=256):
w,h = map(int, size)
self.makeCurrent()
tex = None
fb = None
try:
output = np.empty((w, h, 4), dtype=np.ubyte)
fb = glfbo.glGenFramebuffers(1)
glfbo.glBindFramebuffer(glfbo.GL_FRAMEBUFFER, fb )
glEnable(GL_TEXTURE_2D)
tex = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, tex)
texwidth = textureSize
data = np.zeros((texwidth,texwidth,4), dtype=np.ubyte)
## Test texture dimensions first
glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGBA, texwidth, texwidth, 0, GL_RGBA, GL_UNSIGNED_BYTE, None)
if glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH) == 0:
raise Exception("OpenGL failed to create 2D texture (%dx%d); too large for this hardware." % shape[:2])
## create teture
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texwidth, texwidth, 0, GL_RGBA, GL_UNSIGNED_BYTE, data.transpose((1,0,2)))
self.opts['viewport'] = (0, 0, w, h) # viewport is the complete image; this ensures that paintGL(region=...)
# is interpreted correctly.
p2 = 2 * padding
for x in range(-padding, w-padding, texwidth-p2):
for y in range(-padding, h-padding, texwidth-p2):
x2 = min(x+texwidth, w+padding)
y2 = min(y+texwidth, h+padding)
w2 = x2-x
h2 = y2-y
## render to texture
glfbo.glFramebufferTexture2D(glfbo.GL_FRAMEBUFFER, glfbo.GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0)
self.paintGL(region=(x, h-y-h2, w2, h2), viewport=(0, 0, w2, h2)) # only render sub-region
glBindTexture(GL_TEXTURE_2D, tex) # fixes issue #366
## read texture back to array
data = glGetTexImage(GL_TEXTURE_2D, 0, format, type)
data = np.fromstring(data, dtype=np.ubyte).reshape(texwidth,texwidth,4).transpose(1,0,2)[:, ::-1]
output[x+padding:x2-padding, y+padding:y2-padding] = data[padding:w2-padding, -(h2-padding):-padding]
finally:
self.opts['viewport'] = None
glfbo.glBindFramebuffer(glfbo.GL_FRAMEBUFFER, 0)
glBindTexture(GL_TEXTURE_2D, 0)
if tex is not None:
glDeleteTextures([tex])
if fb is not None:
glfbo.glDeleteFramebuffers([fb])
return output
Somewhere in your code you are setting the center to be an int. setCameraPosition, maybe? It needs to be a Vector object, instead.
Quadtree code are readily available on Github. However most of the Quadtrees (collision detection) work on following criteria:
if No. Points in each Node > Capacity
Divide the node into Four parts
else
Do Not divide further
I am instead looking to apply the following logic. Id there are more than one Instances in a node, divide it further into four until each node has only one instance.
if No. of Instance in each Node > 1
Divide the node into Four parts
else
Do Not Divide further
I already have an image with each pixel labelled as per the instance. For example
Instance Value
Background 0
Chair 1
Table 2
Plant 3
Teddy Bear 4
So with all the pixels labelled, I have an image that looks like this
So if we read this image into an array, each (x,y) coordinate has a value associated.
After quadtree is applied, this is what I would like the quadtree to do to my image. As an example only a portion of quadtree applied on chair is shown for demo
So how do I modify the Quadtree criteria in the following code, copied from Github to achieve what I am looking for?
import numpy as np
import math
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def distanceToCenter(self, center):
return math.sqrt((center.x-self.x)**2 + (center.y-self.y)**2)
class Rectangle:
def __init__(self, center, width, height):
self.center = center
self.width = width
self.height = height
self.west = center.x - width
self.east = center.x + width
self.north = center.y - height
self.south = center.y + height
def containsPoint(self, point):
return (self.west <= point.x < self.east and
self.north <= point.y < self.south)
def intersects(self, range):
return not (range.west > self.east or
range.east < self.west or
range.north > self.south or
range.south < self.north)
def draw(self, ax, c='k', lw=1, **kwargs):
x1, y1 = self.west, self.north
x2, y2 = self.east, self.south
ax.plot([x1,x2,x2,x1,x1], [y1,y1,y2,y2,y1], c=c, lw=lw, **kwargs)
class QuadTree:
def __init__(self, boundary, capacity = 4):
self.boundary = boundary
self.capacity = capacity
self.points = []
self.divided = False
def insert(self, point):
# if the point is in the range of current quadTree
if not self.boundary.containsPoint(point):
return False
# if has not reached capcaity
if len(self.points) < self.capacity:
self.points.append(point)
return True
if not self.divided:
self.divide()
if self.nw.insert(point):
return True
elif self.ne.insert(point):
return True
elif self.sw.insert(point):
return True
elif self.se.insert(point):
return True
return False
def queryRange(self, range):
found_points = []
if not self.boundary.intersects(range):
return []
for point in self.points:
if range.containsPoint(point):
found_points.append(point)
if self.divided:
found_points.extend(self.nw.queryRange(range))
found_points.extend(self.ne.queryRange(range))
found_points.extend(self.sw.queryRange(range))
found_points.extend(self.se.queryRange(range))
return found_points
def queryRadius(self, range, center):
found_points = []
if not self.boundary.intersects(range):
return []
for point in self.points:
if range.containsPoint(point) and point.distanceToCenter(center) <= range.width:
found_points.append(point)
if self.divided:
found_points.extend(self.nw.queryRadius(range, center))
found_points.extend(self.ne.queryRadius(range, center))
found_points.extend(self.sw.queryRadius(range, center))
found_points.extend(self.se.queryRadius(range, center))
return found_points
def divide(self):
center_x = self.boundary.center.x
center_y = self.boundary.center.y
new_width = self.boundary.width / 2
new_height = self.boundary.height / 2
nw = Rectangle(Point(center_x - new_width, center_y - new_height), new_width, new_height)
self.nw = QuadTree(nw)
ne = Rectangle(Point(center_x + new_width, center_y - new_height), new_width, new_height)
self.ne = QuadTree(ne)
sw = Rectangle(Point(center_x - new_width, center_y + new_height), new_width, new_height)
self.sw = QuadTree(sw)
se = Rectangle(Point(center_x + new_width, center_y + new_height), new_width, new_height)
self.se = QuadTree(se)
self.divided = True
def __len__(self):
count = len(self.points)
if self.divided:
count += len(self.nw) + len(self.ne) + len(self.sw) + len(self.se)
return count
def draw(self, ax):
self.boundary.draw(ax)
if self.divided:
self.nw.draw(ax)
self.ne.draw(ax)
self.se.draw(ax)
self.sw.draw(ax)
I want to visualize an algorithm (Graham scan) in python with tkinter.
I want to animate the algorithm and I am stuck.
I basically want to draw and delete lines but I don't understand canvas.after() well enough to make it work.
draw_line() returns the line object but when I call it in canvas.after(..., draw_line, ...) I don't see a way to get the return value or how to call another canvas.after() to change the color/delete that line if the function draw_line() hasn't been called yet because of the delay.
Thanks in advance.
from tkinter import *
import math
import random
class Point:
def __init__(self, _x, _y, _a=0):
self.x = _x
self.y = _y
self.angle = _a
def get_co(self):
return self.x, self.y
def draw_hull(hull):
for i in range(len(hull) - 1):
canvas.create_line(hull[i][0], hull[i][1], hull[i + 1][0], hull[i + 1][1], fill="red", width=2)
def draw_line(p1, p2, color="yellow"):
return canvas.create_line(p1.x, p1.y, p2.x, p2.y, fill=color, width=2)
def convex_hull(list_points):
# find bottom point
bottom_point = Point(math.inf, math.inf)
for point in list_points:
if point[1] < bottom_point.y:
bottom_point = Point(point[0], point[1])
# calculate angles between the bottom point and the other points
points = []
for point in list_points:
if point != bottom_point.get_co():
new_point = Point(point[0], point[1])
angle = calculate_angle(bottom_point, new_point)
new_point.angle = angle
points.append(new_point)
# sort the points by angle
swaps = None
while swaps != 0:
swaps = 0
for i in range(len(points) - 1):
point1 = points[i]
point2 = points[i + 1]
if point1.angle > point2.angle:
points[i], points[i + 1] = points[i + 1], points[i]
swaps += 1
# go through the points and add them to the convex hull
# if the angle between 3 points ever exeeds 180 degrees, discard the middle point
hull = [bottom_point, points[0]]
i = 1
while i < len(points):
####### DRAW LINE #######
canvas.after(i*500, draw_line, hull[-2], hull[-1])
##############
# check angle
angle = calculate_angle(hull[-2], hull[-1], points[i])
if angle == -1:
########## DELETE LINE ##########
# change color of line to red and delete it a bit later
# canvas.itemconfig(line, fill="red")
# canvas.after(i*500+250, canvas.delete, line)
####################
# pop the point of the stack
hull.pop()
else:
########## CHANGE COLOR OF LINE ##########
# change color of line to green
# canvas.itemconfig(line, fill="green")
####################
# move to the next point
hull.append(points[i])
i += 1
# add bottom point again for loop
hull.append(bottom_point)
# give easy return list (with coordinate-tupels not class objects)
output = []
for point in hull:
output.append(point.get_co())
return output
def calculate_angle(point1, point2, point3=None):
if point3 is None:
if point2.x - point1.x == 0:
return 90
elif point2.x - point1.x > 0:
return math.degrees(math.atan((point2.y - point1.y)/(point2.x - point1.x)))
else:
return 180 - math.degrees(math.atan((point2.y - point1.y)/(point1.x - point2.x)))
else:
v1 = Point(point1.x - point2.x, point1.y - point2.y)
v2 = Point(point3.x - point2.x, point3.y - point2.y)
det = (v1.x * v2.y) - (v2.x * v1.y)
if det < 0:
return 1
else:
return -1
window = Tk()
window.geometry("1000x600")
canvas = Canvas(window, width=1000, height=600)
canvas.pack()
POINTSIZE = 2
points = []
for i in range(100):
x = random.randint(50, 950)
y = random.randint(50, 550)
points.append((x, y))
canvas.create_oval(x - POINTSIZE, y - POINTSIZE, x + POINTSIZE, y + POINTSIZE, fill="black")
hull = convex_hull(points)
# draw_hull(hull)
window.mainloop()
If you have questions about the code, let me know. Because I dont know where to start to explain, since I made major changes to your code.
Anyway, I would be glad if you share your code again, once you are done with, on CodeReview and please do let me know. Because it was fun to work with and your code seems incomplete to me.
Happy Coding:
import tkinter as tk
import random
import math
class Point:
def __init__(self, _x, _y, _a=0):
self.x = _x
self.y = _y
self.angle = _a
return None
def get_co(self):
return self.x, self.y
class Window(tk.Tk):
def __init__(self,_w,_h):
super().__init__()
self.POINTSIZE = 2
self.points = []
self.swaps = None
self.count = 1
self.delay = 200
self.title('Graham scan simulation')
self.toolbar = tk.Frame(self,background='grey')
self.refresh_button = tk.Button(self.toolbar,text='Refresh',
command=self.refresh)
self.start_button = tk.Button(self.toolbar,text='Start',
command = self.convex_hull)
self.canvas = tk.Canvas(self,width=_w,height=_h)
self.toolbar.pack(side=tk.TOP,fill=tk.BOTH,expand=True)
self.refresh_button.pack(side=tk.LEFT)
self.start_button.pack(side=tk.LEFT)
self.canvas.pack(side=tk.BOTTOM,fill=tk.BOTH,expand=True)
def generate_points(self):
for point_instance in self.points:
yield point_instance
def find_bottom_point(self):
bottom_point = Point(math.inf,math.inf)
for point in self.generate_points():
if point.y < bottom_point.y:
bottom_point = point
return bottom_point
def calculate_angle(self,point1, point2):
if point2.x - point1.x == 0:
return 90
elif point2.x - point1.x > 0:
return math.degrees(math.atan((point2.y - point1.y)/(point2.x - point1.x)))
else:
return 180 - math.degrees(math.atan((point2.y - point1.y)/(point1.x - point2.x)))
def calculate_angels_by_bottom_point(self,bottom_point):
for point in self.generate_points():
if point != bottom_point:
angle = self.calculate_angle(bottom_point,point)
point.angle = angle
def sort_points(self,event_variable):
if self.swaps != 0:
self.swaps = 0
for i in range(len(self.points)-1):
point1 = self.points[i]
point2 = self.points[i + 1]
if point1.angle > point2.angle:
self.points[i], self.points[i + 1] = self.points[i + 1], self.points[i]
self.swaps += 1
if self.swaps == 0:
event_variable.set(1)
self.after(20,self.sort_points,event_variable)
def check_angle(self,point1,point2,point3):
v1 = Point(point1.x - point2.x, point1.y - point2.y)
v2 = Point(point3.x - point2.x, point3.y - point2.y)
det = (v1.x * v2.y) - (v2.x * v1.y)
if det < 0:
return 1
else:
return -1
def draw_line(self,p1,p2,color='yellow'):
return self.canvas.create_line(p1.x,p1.y, p2.x,p2.y, fill='yellow',tags='line')
def clear_red_lines(self,p1,p2):
shapes = self.canvas.find_withtag('line')
for shape in shapes:
if self.canvas.itemcget(shape,'fill') == 'red':
coords = self.canvas.coords(shape)
overlapped = self.canvas.find_overlapping(*coords)
for i in overlapped:
coords2 = self.canvas.coords(i)
if coords == coords2:
self.canvas.delete(i)
self.canvas.delete(shape)
def animated_draw(self,hull):
if self.count != len(self.points):
line = self.draw_line(hull[-2],hull[-1])
check_mark = self.check_angle(hull[-2],hull[-1],self.points[self.count])
if check_mark == -1:
self.canvas.itemconfig(line,fill='red')
self.after(self.delay-100,lambda:self.clear_red_lines(hull[-2],hull[-1]))
hull.pop()
else:
self.canvas.itemconfig(line,fill='green')
hull.append(self.points[self.count])
self.count += 1
self.after(self.delay,self.animated_draw,hull)
def convex_hull(self):
bottom_point = self.find_bottom_point()
self.calculate_angels_by_bottom_point(bottom_point)
event_variable = tk.IntVar(self,value=0)
self.sort_points(event_variable)
self.wait_variable(event_variable)
self.points.pop(0)
self.animated_draw(hull = [bottom_point, self.points[0]])
def generate_listpoints(self,_amount):
'''using a generator for memory purpose'''
for i in range(_amount):
x = random.randint(50, 950)
y = random.randint(50, 550)
yield x,y
def refresh(self):
self.swaps = None
self.count = 1
self.points= []
self.populate_canvas()
def populate_canvas(self):
self.canvas.delete('all')
for x,y in self.generate_listpoints(100):
self.points.append(Point(x, y))#instead of creating throwing instances away.
self.canvas.create_oval(x - self.POINTSIZE,
y - self.POINTSIZE,
x + self.POINTSIZE,
y + self.POINTSIZE,
fill="black")
root = Window(1000,600)
root.mainloop()
I'm trying to put my button borderless so we can we see the progressbar on the background
I tried all the forum but coundt put the border of the button round or make something like the second picture
this is the part of the code for the button :
bg3 = Image.open("blanc.png")
resized_bg03 = bg3.resize((20, 20),Image.ANTIALIAS)
new_bg03 = ImageTk.PhotoImage(resized_bg03)
s = Button(Fenetre,image=new_bg03,borderwidth=0,highlightthickness=0)
s.place(x=600, y=10,height = 20 , width = 20)
I would like to have something more like this with a circle and no border :
can someone help me :)
Try this:
import tkinter as tk
# Kindly plagiarised from: https://stackoverflow.com/a/17985217/11106801
def _create_circle(self, x, y, r, **kwargs):
return self.create_oval(x-r, y-r, x+r, y+r, **kwargs)
tk.Canvas.create_circle = _create_circle
# Please suggest a better name for the class
class ProgressBar(tk.Canvas):
def __init__(self, height=20, radius=6, width=400, circle_colour="black",
colour="red", bar_width=6, bd=0, highlightthickness=0,
anti_bar_colour="white", **kwargs):
super().__init__(height=height, width=width, bd=bd,
highlightthickness=highlightthickness, **kwargs)
self.radius = radius
self.height = height
self.width = width
self.rectangle = None
self.circle_colour = circle_colour
self.colour = colour
self.bar_width = bar_width
self.circle = None
self.button_1_down = False
self.create_anti_bar(anti_bar_colour)
self.progress = 0
super().bind("<Enter>", self.show_circle)
super().bind("<Leave>", self.hide_circle)
super().bind("<Button-1>", self.mouse_click)
super().bind("<ButtonRelease-1>", self.mouse_release)
super().bind("<B1-Motion>", self.mouse_motion)
def create_anti_bar(self, colour):
start_y = (self.height - self.bar_width)/2
end_y = self.height - start_y
start_x = self.radius
end_x = self.width - self.radius
# Change it to >= if you don't what the bar to appear when the
# progress is at 0
if start_x > end_x:
return None
super().create_rectangle(start_x, start_y, end_x, end_y,fill=colour,
outline=colour)
def mouse_click(self, event):
self.button_1_down = True
self.progress = (event.x - self.radius)/(self.width - 2*self.radius)
def mouse_release(self, event=None):
self.button_1_down = False
def mouse_motion(self, event):
if self.button_1_down:
self.mouse_click(event)
def hide_circle(self, event=None):
if self.circle is not None:
super().delete(self.circle)
self.circle = None
def show_circle(self, event=None):
# Try removing the circle if we can
self.hide_circle()
x = (self.width - 2*self.radius)*self._progress + self.radius
self.circle = super().create_circle(x, self.height//2, self.radius,
fill=self.circle_colour,
outline=self.circle_colour)
def update_bar(self):
# Try removing the progress bar
if self.rectangle is not None:
super().delete(self.rectangle)
start_y = (self.height - self.bar_width)/2
end_y = self.height - start_y
start_x = self.radius
end_x = (self.width - 2*self.radius)*self._progress + self.radius
# Change it to >= if you don't what the bar to appear when the
# progress is at 0
if start_x > end_x:
return None
self.rectangle = super().create_rectangle(start_x, start_y, end_x,
end_y, fill=self.colour,
outline=self.colour)
#property
def progress(self):
return self._progress
#progress.setter
def progress(self, new_value):
# Check if the new_value is in the correct range
if new_value < 0:
new_value = 0
elif new_value > 1:
new_value = 1
# Update self._progress
self._progress = new_value
# Update the progress bar
self.update_bar()
# If the circle was shown update it
if self.circle is not None:
self.show_circle()
if __name__ == "__main__":
root = tk.Tk()
pbar = ProgressBar()
pbar.pack()
pbar.progress = 0
# keep incrementing the progress until the end then stop
def increment_progress():
pbar.progress += 0.001
if pbar.progress >= 1:
return None
pbar.after(10, increment_progress)
increment_progress()
root.mainloop()
Tell me if you don't get what any of the methods do. It is too much code to properly annotate
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
I have a compiler error “not defined” although there is a definition
from gasp import *
GRID_SIZE = 30
MARGIN = GRID_SIZE
BACKGROUND_COLOR = color.BLACK # Colors we use
WALL_COLOR = (0.6 * 255, 0.9 * 255, 0.9 * 255)
# The shape of the maze. Each character
# represents a different type of object
# % - Wall
# . - Food
# o - Capsule
# G - Ghost
# P - Chomp
# Other characters are ignored
the_layout = [
"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%",
"%.....%.................%.....%",
"%o%%%.%.%%%.%%%%%%%.%%%.%.%%%o%",
"%.%.....%......%......%.....%.%",
"%...%%%.%.%%%%.%.%%%%.%.%%%...%",
"%%%.%...%.%.........%.%...%.%%%",
"%...%.%%%.%.%%% %%%.%.%%%.%...%",
"%.%%%.......%GG GG%.......%%%.%",
"%...%.%%%.%.%%%%%%%.%.%%%.%...%",
"%%%.%...%.%.........%.%...%.%%%",
"%...%%%.%.%%%%.%.%%%%.%.%%%...%",
"%.%.....%......%......%.....%.%",
"%o%%%.%.%%%.%%%%%%%.%%%.%.%%%o%",
"%.....%........P........%.....%",
"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"]
class Immovable:
def is_a_wall(self):
return False
class Nothing(Immovable):
pass
class Maze:
def __init__(self):
self.have_window = False
self.game_over = False
self.set_layout(the_layout)
set_speed(20)
def set_layout(self, layout):
height = len(layout)
width = len(layout[0])
self.make_window(width, height)
self.make_map(width, height)
max_y = height - 1
for x in range( width ):
for y in range(height):
char = layout[max_y - y][x]
self.make_object((x, y), char)
def make_window(self, width, height):
grid_width = (width -1) * GRID_SIZE
grid_height = (height - 1) * GRID_SIZE
screen_width = 2 * MARGIN + grid_width
screen_height = 2 * MARGIN + grid_height
begin_graphics(screen_width, screen_height,"Chomp",BACKGROUND_COLOR)
def to_screen(self, point):
(x,y) = point
x = x * GRID_SIZE + MARGIN
y = y * GRID_SIZE + MARGIN
return(x,y)
def make_map(self, width, height):
self.width = width
self.height = height
self.map = []
for y in range(width):
new_row = []
for x in range(width):
new_row.append(Nothing())
self.map.append(new_row)
def make_object(self,point,charactor):
(x,y) = point
if charactor == "%":
self.map[y][x] = Wall(self,point)
def finished(self):
return self.game_over
def play(self):
update_when('next_tick')
def done(self):
end_graphics()
self.map = []
def object_at(self,point):
(x,y) = point
if y < 0 or y >= self.height:
return Nothing()
if x < 0 or x >= self.width:
return Nothing()
return self.map[y][x]
class Wall(Immovable):
def __init__(self, maze, point):
self.place = point # Store our position
self.screen_point = maze.to_screen(point)
self.maze = maze # Keep hold of Maze
self.draw()
def draw(self):
(screen_x, screen_y) = self.screen_point
dot_size = GRID_SIZE * 0.2
Circle(self.screen_point, dot_size,
color = WALL_COLOR, filled = 1)
(x, y) = self.place
neighbors = [ (x+1, y), (x-1, y)]
for neighbor in neighbors:
self.check_neighbor(neighbor)
def check_neighbor(self,neighbor):
maze = self.maze
object = maze.object_at(neighbor)
if object.is_a_wall():
here = self.screen_point
there = maze.to_screen(neighbor)
Line(here, there, color = WALL_COLOR,thickness = 2)
def is_a_wall(self):
return True
the_maze = Maze()
while not the_maze.finished():
the_maze.play()
the_maze.done()
I got this error..
Traceback (most recent call last): File "chomp.py", line 110, in
class Wall(Immovable): File "chomp.py", line 124, in Wall
for neighbor in neighbors: NameError: name '
neighbors' is not defined
I spent lot of time still can't find what's wrong, need some help
You never close the function call to Circle() two lines about line 122, that's probably it. You're probably missing an argument based on the trailing comma.
dot_size = GRID_SIZE * 0.2
Circle(self.screen_point, dot_size, # No closing parentheses
(x, y) = self.place
neighbors = [ (x+1, y), (x-1, y)]
for neighbor in neighbors:
self.check_neighbor(neighbor)
Circle(self.screen_point, dot_size,
missing something at the end of that line