Godot 3.1 telling me that lock() and get_pixel() are nonexistent functions - godot

I'm attempting to generate a height map from a noise texture. As far as I understand, in order to call get_pixel() on an image in this context, the image must first be locked. However, when I attempt to run the program, it exits with the error: Invalid call. Nonexistent function 'lock' in base 'StreamTexture'.
If I attempt to run it without locking the image, I get the error: Invalid call. Nonexistent function 'get_pixel' in base 'StreamTexture'.
I am certain that the instructions that I am following are for the same version of Godot I am running (3.1), so why is the engine telling me that lock() and get_pixel() are nonexistent functions?
My code is here:
extends Spatial
var width
var height
var heightData = {}
var vertices = PoolVector3Array()
var drawMesh = Mesh.new()
func _ready():
var noiseTexture = load("res://noiseTexture.png")
width = noiseTexture.get_width()
height = noiseTexture.get_height()
noiseTexture.lock()
for x in range(0, width):
for y in range(0, height):
heightData[Vector2(x,y)] = noiseTexture.get_pixel(x,y).r
noiseTexture.unlock()
for x in range(0, width-1):
for y in range(0, height-1):
createQuad(x,y)
var surfTool = SurfaceTool.new()
surfTool.begin(Mesh.PRIMITIVE_TRIANGLES)
for i in vertices.size():
surfTool.add_vertex(vertices[i])
surfTool.commit(drawMesh)
$MeshInstance.mesh = drawMesh
func createQuad(x,y):
#First half
vertices.push_back(Vector3(x, heightData[Vector2(x,y)], -y))
vertices.push_back(Vector3(x, heightData[Vector2(x,y+1)], -y-1))
vertices.push_back(Vector3(x+1, heightData[Vector2(x+1,y+1)], -y-1))
#Second Half
vertices.push_back(Vector3(x, heightData[Vector2(x,y)], -y))
vertices.push_back(Vector3(x+1, heightData[Vector2(x+1,y+1)], -y-1))
vertices.push_back(Vector3(x+1, heightData[Vector2(x+1,y)], -y))
Any help is greatly appreciated.
EDIT - I have (tried) to implement the changes that were suggested in the comments (yet I still don't know what to do with the color variable) and have attached a screenshot of my resulting code, as well as some comments I have made to try and explain to myself why the process SHOULD be working (I think). It also shows my node structure, which is why I opted to display this as an image. However, when I try to run this, the program crashes with the error displayed.

Check the docs;
StreamTexture does not have the method lock.
I think the class you are looking to use is Image. The Texture class is typically intended for drawing on the screen or applying to a Material
var noiseImage = Image.new()
noiseImage.load("res://noiseTexture.png")
noiseImage.lock() # Lock the image here
var color = noiseImage.get_pixel(10, 10) # Replace with your height map population
PS:
Just to let you know, I had a lot of issues with memory usage here so make sure you test that also (C# has bad garbage collector though).
You might need to dispose of the image, surface tool, and array mesh (If you remove the terrain object) to maintain optimum performance.

I have run into similar issues with generating heightmap terrains.
nathanfranke is correct and his solution will work.
If you for some reason use an ImageTexture you can call get_data() on that to get the underlying Image. Then you call lock() on the Image just like nathan says in his answer.
Take care to check that your coordinates for get_pixel() are correct. You can set a breakpoint by clicking on the very left edge of the line of code.
I mention this because I was very frustrated until I realized that my coordinate calculations were all int which resulted in the sampled pixel always being at <0,0>.
Here is part of my code for sampling an image into the HeightMapShape.map_data for Bullet heightmap collisions:
var map_w = shape.map_width
var map_h = shape.map_depth
var img_w = img.get_width()
var img_h = img.get_height()
img.lock()
for y in range(0, map_h):
py = float(img_h) * (float(y) / float(map_h))
for x in range(0, map_w):
px = float(img_w) * (float(x) / float(map_w))
index = y * img_h + x
pix = img.get_pixel(px, py)
shp.map_data[index] = pix.r * heightmap_height + heightmap_offset

Related

How to pass HumanCurve to HumanClicker using pyclick package?

from pyclick import HumanClicker
hc = HumanClicker()
hc.move((100,100),2)
hc.click()
You can also customize the mouse curve by passing a HumanCurve to HumanClicker. You can control:
number of internal knots, to change the overall shape of the curve,
distortion to simulate shivering,
tween to simulate acceleration and speed of movement
I am currently using python 3.6 but i am facing a problem, I don't know How to pass HumanCurve to HumanClicker can someone help me?
You can do so by doing the following:
# get the current position
fromPoint = pyautogui.position()
# the final position of the mouse
toPoint = (100,100)
# Take a look at all the options in this function: https://github.com/patrikoss/pyclick/blob/master/pyclick/humancurve.py#L18
options = {
"knotsCount": 2,
}
human_curve = HumanCurve(fromPoint=fromPoint, toPoint=toPoint, **options)
# initialize HumanClicker object
hc = HumanClicker()
# use the move() function with the your custom curve
hc.move((x,y), duration=1, humanCurve=human_curve)

Adding GraphicsObject to two ViewBoxes or QGraphicsViews

Ultimately I want pyqtgraph to display a single GraphicsObject simultaneously in several ViewBoxes, sharing a single scene.
At the same time I want to have some other GraphicsObjects in a single ViewBox only.
Something like this:
vb0 = ViewBox()
vb1 = ViewBox()
# shown only in first ViewBox vb0
local_item = GraphicsObject()
# shown in all ViewBoxes
global_item = GraphicsObject()
vb0.addItem(local_item)
assert vb0.scene() is vb1.scene()
# the magic function i am looking for
vb0.scene().addItemGlobally(global_item)
So very naively I looked into the ViewBox sourcecode and reproduced the steps for addItem() like here:
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore as qc, QtGui as qg, QtWidgets as qw
class Polygon(pg.GraphicsObject):
"""Just a Triangle..."""
app = qw.QApplication([])
viewer = pg.GraphicsWindow()
vb0 = viewer.addViewBox(0, 0)
vb1 = viewer.addViewBox(0, 1)
viewer.show()
poly_yellow = Polygon((125, 125, 0, 255))
scene = vb0.scene()
added_items = vb1.addedItems = vb0.addedItems
child_group = vb1.childGroup = vb0.childGroup
child_group.setParent(scene)
child_group.itemsChangedListeners.append(vb1)
# here reproducing steps found in addItem()
if scene is not poly_yellow.scene():
scene.addItem(poly_yellow)
poly_yellow.setParentItem(child_group)
added_items.append(poly_yellow)
vb0.updateAutoRange()
# vb1.updateAutoRange()
# checking if ViewBoxes share relevant attributes
assert vb0.scene() is vb1.scene()
assert vb0.scene() is poly_yellow.scene()
assert vb0.addedItems is vb1.addedItems
assert vb0.childGroup is vb1.childGroup
app.exec_()
Running this gives me two ViewBoxes, but only vb0 showing the triangle. Also this approach would give me global only Items. Is there any way to get something like local/global items without re-implementing ViewBoxes completely?
EDIT: I think it is impossible to achieve what I want with pyqtgraph ViewBoxes. A transform of the global items must happen just before the painting.
I found it is not easily doable using ViewBox. However, it is possible to us pyqtgraphs GraphicsView, implementing a lot of functionality found in the ViewBox class already.
My approach now is to generate as many GraphicsView as I need, and set them all to one scene via GraphicsView.setScene()
The scene contains the 'global' items, displayed in every View. The Viewspecific, local Items are drawn using the GraphicsView.drawBackground() function.
I haven't tested it to much, but it seems quite good working, for the case, were several thousend items are added to the scene, but only few items are drawn to the background.

(Python 3 / OpenCV) cv2.imwrite returns a Boolean and not the image object

I am trying to edit a color image and return the gray scale of that image. Which I am able to do but I can only get to it by hard coding the name. After a little print statement debugging I found that the return statement I was using in only returning a boolean.
def change_to_gray_scale(path):
color_image = path
img = cv2.imread(color_image, 0)
gray_img = cv2.imwrite(color_image.strip(".jpg") + "_colorless.jpg", img)
return gray_img # Returns a Boolean and not "new_image_colorless.jpg"
What I wanted to do it get it to return the new image but I have had no luck. I have been at this for a few hours now and just might be making a silly little mistake. So while I take a break I thought I would let some other eyes take a look at it.
Thanks for the help!
For the time being I have just decided to go with a little work around string formatting. CV2 documentation for C says that imwrite returns a bool so I'm just going to assume that is the same for python.
Here is the work around I came up with.
image_str = (image_path.strip(".jpg") + "_colorless.jpg")
image_name = os.path.split(image_str)[1]
In the future I will put this into function above.

Continually running functions in tkinter after mainloop in Python 3.4

What I want to do is colour in a single pixel in the centre of the screen, then at random choose an adjacent pixel and colour it in, and then repeat until some condition is met - anything such as time, or the screen is full, or after a certain number of pixels are full. This ending isn't too important, I haven't got that far yet, and I think I could manage to work that out myself.
I have no experience with tkinter, but I decided it was the best way to display this, since I don't really no any other way. Some of this code (mainly the tkinter functions like Canvas, PhotoImage etc) is therefore copy-pasted (and slightly edited) from examples I found here.
What my code does when run is hard to tell - it uses the CPU as much as it can seemingly indefinitely, and slowly increases its memory usage, but doesn't appear to do anything. No window opens, and the IDLE interpreter goes to a blank line, as usual when calculating something. When killed, the window opens, and displays a white page with a little black blob in the bottom right corner - as if the program had done what it was meant to, but without showing it happening, and starting in the wrong place.
So:
Why does it do this?
What should I do to make my program work?
What would be a better way of coding this, changing as many things as you like (ie. no tkinter, a different algorithm etc)?
from tkinter import Tk, Canvas, PhotoImage, mainloop
from random import randrange
from time import sleep
def choose_pixel(pixel_list):
possible_pixels = []
for x in pixel_list:
#adjacent pixels to existing ones
a = [x[0] + 1, x[1]]
b = [x[0] - 1, x[1]]
c = [x[0], x[1] + 1]
d = [x[0], x[1] - 1]
#if a not in pixel_list:
possible_pixels.append(a)
#if b not in pixel_list:
possible_pixels.append(b)
#if c not in pixel_list:
possible_pixels.append(c)
#if d not in pixel_list:
possible_pixels.append(d)
pixel_choosing = randrange(len(possible_pixels))
final_choice = possible_pixels[pixel_choosing]
return final_choice
def update_image(img_name, pixel):
img.put("#000000", (pixel[0], pixel[1]))
WIDTH, HEIGHT = 320, 240
window = Tk()
#create white background image
canvas = Canvas(window, width=WIDTH, height=HEIGHT, bg="#ffffff")
canvas.pack()
img = PhotoImage(width=WIDTH, height=HEIGHT)
canvas.create_image((WIDTH, HEIGHT), image=img, state="normal")
first_pixel = [int(WIDTH/2), int(HEIGHT/2)]
pixel_list = [first_pixel]
img.put("#000000", (first_pixel[0], first_pixel[1]))
canvas.pack()
runs = 0
while True:
next_pixel = choose_pixel(pixel_list)
pixel_list.append(next_pixel)
window.after(0, update_image, img, next_pixel)
canvas.pack()
runs+=1
window.mainloop()
The pattern for running something periodically in tkinter is to write a function that does whatever you want it to do, and then the last thing it does is use after to call itself again in the future. It looks something like this:
import tkinter as tk
...
class Example(...):
def __init__(self, ...):
...
self.canvas = tk.Canvas(...)
self.delay = 100 # 100ms equals ten times a second
...
# draw the first thing
self.draw_something()
def draw_something(self):
<put your code to draw one thing here>
self.canvas.after(self.delay, self.draw_something)
After the function draws something, it schedules itself to run again in the future. The delay defines approximately how long to wait before the next call. The smaller the number, the faster it runs but the more CPU it uses. This works, because between the time after is called and the time elapses, the event loop (mainloop) is free to handle other events such as screen redraws.
While you may think this looks like recursion, it isn't since it's not making a recursive call. It's merely adding a job to a queue that the mainloop periodically checks.

VTK: how can I add a scrollbar to my project?

What's the easiest way to add a scrollbar to my VTK project ?
thanks
Update
def vtkSliderCallback2(obj, event):
sliderRepres = obj.GetRepresentation()
pos = sliderRepres.GetValue()
contourFilter.SetValue(0, pos)
SliderRepres = vtk.vtkSliderRepresentation2D()
min = 0 #ImageViewer.GetSliceMin()
max = 256 #ImageViewer.GetSliceMax()
SliderRepres.SetMinimumValue(min)
SliderRepres.SetMaximumValue(max)
SliderRepres.SetValue((min + max) / 2)
SliderRepres.SetTitleText("Slice")
SliderRepres.GetPoint1Coordinate().SetCoordinateSystemToNormalizedDisplay()
SliderRepres.GetPoint1Coordinate().SetValue(0.2, 0.6)
SliderRepres.GetPoint2Coordinate().SetCoordinateSystemToNormalizedDisplay()
SliderRepres.GetPoint2Coordinate().SetValue(0.4, 0.6)
SliderRepres.SetSliderLength(0.02)
SliderRepres.SetSliderWidth(0.03)
SliderRepres.SetEndCapLength(0.01)
SliderRepres.SetEndCapWidth(0.03)
SliderRepres.SetTubeWidth(0.005)
SliderRepres.SetLabelFormat("%3.0lf")
SliderRepres.SetTitleHeight(0.02)
SliderRepres.SetLabelHeight(0.02)
SliderWidget = vtk.vtkSliderWidget()
SliderWidget.SetInteractor(iren)
SliderWidget.SetRepresentation(SliderRepres)
SliderWidget.KeyPressActivationOff()
SliderWidget.SetAnimationModeToAnimate()
SliderWidget.SetEnabled(True)
SliderWidget.AddObserver("InteractionEvent", vtkSliderCallback2)
Just to be complete and for other users googling thing.
vtkSliderWidget will do what you want if you need it to set a value.
//edit based on your edit
If you want to get the value, you have to connect an event to the slider which is fired when the value is changed. Than retrieve this value and update accordingly. An example in C++ is found here
// I actually think my issue is that the callback function is invoked for each thumb position when I slide it. How can avoid that ? In other words I only want the last position to trigger the callback function...
Try coupling it to the EndInteractionEvent instead of to the InteractionEvent.
SliderWidget.AddObserver("EndInteractionEvent", vtkSliderCallback2)
// stuff
By the way, if you use python and VTK and need GUI stuff, I advice you to use the python QT and python qt widgets which ease up alot of this stuff. Some code of one of my old projects using QT+Python+VTK for GUI + python stuff:
self.verticalSlider = QtGui.QSlider(self.centralwidget)
self.verticalSlider.setOrientation(QtCore.Qt.Vertical)
self.verticalSlider.setObjectName("verticalSlider")
self.horizontalLayout.addWidget(self.verticalSlider)
// connect slider to a method onValueChange
QObject.connect(self.verticalSlider, SIGNAL("valueChanged(int)"),
self.setFibreVolumeOpacity)
def setFibreVolumeOpacity(self, value):
// do stuff here with slider value.

Resources