Laser cutting workflow - Automating taking a picture to use as canvas - Python script

Laser cutting workflow - Automating taking a picture to use as canvas - Python script

jordy_keller
Enthusiast Enthusiast
700 Views
8 Replies
Message 1 of 9

Laser cutting workflow - Automating taking a picture to use as canvas - Python script

jordy_keller
Enthusiast
Enthusiast

Hi,

 

We are often asked to laser cut foam to accommodate items like in the example below.

So far I managed to write a script that imports an image and places it as a canvas.

 

I'd love to get some feedback on my proposed workflow:

- Mount a USB camera to a (3m high) ceiling, pointed straight down at a static surface.

- Write a Python script that takes a picture, scales it and places it as a canvas

 

(Since it's a static surface I can calibrate the script to scale the canvas)

 

Then we could:

- Arrange all items on the surface

- Have the script take a picture and place it as a canvas

- Sketch the contours

- Export the DXF for laser cutting

 

Searching as I post I find that Fusion has it's own version of Python located in:

C:\Users\...\AppData\Local\Autodesk\webdeploy\production\57........6d\Python\

And this changes with every update. Since I will probably need a Python library to take/process a picture, how do I handle this?

 

Thanks for your feedback.

 

jordy_keller_0-1720534876947.png

 

0 Likes
701 Views
8 Replies
Replies (8)
Message 2 of 9

Jorge_Jaramillo
Collaborator
Collaborator

Hi,

 

In the following thread https://forums.autodesk.com/t5/fusion-api-and-scripts/import-data-from-excel-type-xlsx/m-p/11249210#... I posted a solution on how to deal with python modules without installing them in the Fusion 360 production environment which is updated with every new release.

 

I hope this could help you.

 

Regards,

Jorge Jaramillo

 

 

Message 3 of 9

jordy_keller
Enthusiast
Enthusiast

Thanks for the reply.

 

But this seems rather complicated.

 

Here are my thoughts, correct me if I'm wrong ok?

And I sure would like to know if this is possible and if so, how.

 

- Fusion uses it's own virtual Python environment right?

- Say I install the external libraries I need, onto the systems 'main Python'

- In Fusion I write a script A that calls script B:

- Script B will be on my system and uses the systems 'main Python' (that can use the external libraries)

- From my Fusion script I call script B to run in the systems environment (having access to imaging libraries)

- Script B runs, takes a picture and stores it in a known location

- The Fusion script (A) should be able to retrieve the jpg when script B has been run successfully.

 

To test this I have the Fusion script below.

But this gives me this error: PermissionError: [WinError 5] Access is denied

 

So my question at the moment is how to prevent that error and run (system) script B from the Fusion script?

 

 

import adsk.core, adsk.fusion, adsk.cam, traceback
import subprocess

def run(context):
	
	app = adsk.core.Application.get()
	ui  = app.userInterface
	   
	try:
		
		# Run the script and wait for it to complete
		python_path = "C:/Users/......./AppData/Local/Programs/Python/Python312"
		script_path = "D:/Python_testscript/remote_script.py"
		result = subprocess.run([python_path, script_path], capture_output=True, text=True)

		# Check if the script ran successfully
		if result.returncode == 0:
			print("Script ran successfully")
			print("Output:", result.stdout)
		else:
			print("Script encountered an error")
			print("Error:", result.stderr)

	except:
		if ui:
			ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

 

The complete error message:

 

Failed:

Traceback (most recent call last):

File "C:/Users/......./AppData/Roaming/Autodesk/Autodesk Fusion 360/API/Scripts/run_main_script/run_main_script.py", line 17, in run

result = subprocess.run([python_path, script_path], capture_output=True, text=True)

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

File "C:\Users\.......\AppData\Local\Autodesk\webdeploy\production\b7...6d\Python\lib\subprocess.py", line 548, in run

with Popen(*popenargs, **kwargs) as process:

^^^^^^^^^^^^^^^^^^^^^^^^^^^

File "C:\Users\.......\AppData\Local\Autodesk\webdeploy\production\b7...d\Python\lib\subprocess.py", line 1024, in __init__

self._execute_child(args, executable, preexec_fn, close_fds,

File "C:\Users\.......\AppData\Local\Autodesk\webdeploy\production\b7...6d\Python\lib\subprocess.py", line 1493, in _execute_child

hp, ht, pid, tid = _winapi.CreateProcess(executable, args,

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

File "C:\Users\.......\.vscode\extensions\ms-python.debugpy-2024.6.0-win32-x64\bundled\libs\debugpy\_vendored\pydevd\_pydev_bundle\pydev_monkey.py", line 901, in new_CreateProcess

return getattr(_subprocess, original_name)(app_name, cmd_line, *args)

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

PermissionError: [WinError 5] Access is denied


 

0 Likes
Message 4 of 9

jordy_keller
Enthusiast
Enthusiast

Additional info:

 

My 'script B' that is called by Fusion creates a text file with the name that show current date and time:

"2024-07-09_20.13.42.txt"

This script runs ok and works.

 

from datetime import datetime
now = datetime.now()
filename = f'{now.year}-{now.month:02d}-{now.day:02d}_{now.hour:02d}.{now.minute:02d}.{now.second:02d}'
with open(f'{filename}.txt', 'w') as file: pass
0 Likes
Message 5 of 9

Jorge_Jaramillo
Collaborator
Collaborator

Hi,

 

To solve the "Access is denied" error you're missing the "/python.exe" at the end of line #12.

It worked in my end:

Jorge_Jaramillo_0-1720551191130.png

 

Regarding the way I exposed the solution to import other modules, it was done that way because at that moment Fusion 360 was using Python version 3.9.x while the current was above 3.11.x (a year ago).

Now that Fusion 360 uses Python's 3.11.x it is almost compatible with current standard external modules and could be imported from the standard location using the PATH explained in my post.

 

I'll suggest to transform and run your script as a module instead of a external spawn process, inside Fusion 360's python executable.  It'll run faster and safer with the current Python version.

 

I hope this could help out.

 

Regards,

Jorge Jaramillo

 

Message 6 of 9

jordy_keller
Enthusiast
Enthusiast

Thanks for mentioning the missing '/python.exe'

I can now run the script without the permission error.

 

I guess the key point in your post is 'transform and run your script as a module'.

So far I've written only stand-alone scripts in Python so this is something I would have to read up on.

 

I have little time the next few days but I will try to works on this and I will post back here with the result, in a few days that is.

 

Thanks.

0 Likes
Message 7 of 9

jordy_keller
Enthusiast
Enthusiast

Ok,

 

At this point I have an external script that captures an image with my webcam.

The capture is saved as a jpg and I would like fusion to retrieve it and place it as a canvas.

 

But I get an error when trying to retrieve the picture tp put it on the canvas, my full code is below but this diagram shows the problem:

 

 

jordy_keller_0-1720860286076.png

1: Start remote script to do a capture. The script runs fine and saves a jpg.

2: Pass the image location back to Fusion as result.stdout

3: The messagebox show the filepath is received 

4: Filepath is stored in variable 'imagePath'

5: Error when trying to retrieve the image and placing it as a canvas

 

However, when I change the filepath to an image that already exists:

works.jpg

The image is placed as a canvas without any error.

 

I cannot figure out what is wrong here...

 

Thanks

 

 

 

 

 

 

0 Likes
Message 8 of 9

jordy_keller
Enthusiast
Enthusiast

My scripts:

1: Fusion

2: Remote

 

import adsk.core, adsk.fusion, adsk.cam, traceback
import subprocess

def run(context):
	
	app = adsk.core.Application.get()
	ui  = app.userInterface

	imagePath = ''
	   
	try:
		
		# Path to main python install that has cv2 installed
		python_path = "C:/Users/xxxxxxx/AppData/Local/Programs/Python/Python312/python.exe"
		# Path to remote script
		script_path = "D:/remote_script/capture.py"

		result = subprocess.run([python_path, script_path], capture_output=True, text=True)

		ui.messageBox(f'returncode: {result.returncode} \r\nstdout from remote script:\r\n {result.stdout}')

		# Check if the script ran successfully
		if result.returncode == 0:
			imagePath = result.stdout
		else:
			ui.messageBox(f'ERROR: {result.returncode}  {result.stderr}')

	except:
		if ui:
			ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

	try:

		des: adsk.fusion.Design = app.activeProduct
		root = des.rootComponent

		# Define the origin and X and Y direction vectors and
		# use Matrix2D.setWithCoordinateSystem to set the matrix.
		origin = adsk.core.Point2D.create(0,0)

		xDir = adsk.core.Vector2D.create(90, 0)										# Width  of canvas in centimeters
		yDir = adsk.core.Vector2D.create(0, 60)										# Height of canvas in centimeters
		matrix = adsk.core.Matrix2D.create()
		matrix.setWithCoordinateSystem(origin, xDir, yDir)

		# Select placement p:

		p = 3
		
		if p == 1: x, y = -90,   0
		if p == 2: x, y =   0,   0
		if p == 3: x, y = -90, -60
		if p == 4: x, y =   0, -60

		matrix.setCell(0,2, x)
		matrix.setCell(1,2, y)

		canvasInput = root.canvases.createInput(imagePath, root.xYConstructionPlane)
		canvasInput.transform = matrix
		canvas = root.canvases.add(canvasInput)

		# Create a new sketch on the XY plane
		sketches = root.sketches
		xyPlane = root.xYConstructionPlane
		sketch = sketches.add(xyPlane)

		sketch.name = 'Contours'

	except:
		if ui:
			ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

 

Remote:

import cv2
from datetime import datetime


if __name__ == '__main__':

    now = datetime.now()
    filename = f'capture{now.hour:02d}{now.minute:02d}{now.second:02d}'

    cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)		# Webcam capture
    _, img = cap.read()								# Get image
    
    dest = 'd:/remote_script/'						# Destination folder
    
    cv2.imwrite(f'{dest}{filename}.jpg', img)		# Save as jpg
    print(f'{dest}{filename}.jpg')					# stdout string to Fusion script 
    cap.release()
0 Likes
Message 9 of 9

jordy_keller
Enthusiast
Enthusiast

I have a workaround. Instead of passing the file path I pass the directory that the image is saved in.

The fusion script searches that directory and selects the file with the latest timestamp:

 

		jpg_files = [f for f in os.listdir(img_dir.rstrip()) if f.endswith('.jpg')]
		jpg_files.sort(reverse=True)
		imagePath = f'{img_dir.rstrip()}/{jpg_files[0]}'

		try:
			canvasInput = root.canvases.createInput(imagePath, root.xYConstructionPlane)
			canvasInput.transform = matrix
			canvas = root.canvases.add(canvasInput)
		except:
			ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

 

For some reason I have to use rstrip() or I get an error saying there's an extra '\n' in img_dir.

 

Still I do not understand why passing the full path from my remote script doesn't work.