Generating advanced image analysis code#

In case chatGPT is not aware how to do specific image processing steps, we need to provide further details. In this notebook we ask it to generate code that segments blobs and connects close neighbors using a mesh. This meshing-operation is more challenging and thus, we do it in two attempts: First, we provide a simple prompt just asking for doing it. In a second attempt, we provide details how to do it in more detail.

Note: Executing this notebook multiple times may lead to the OpenAI API crashing with an error mentioning that the service is saturated. It presumably blocks repetetive requests with identical prompts. In that case, wait for some time before trying again.

We define a helper function for making prompts first.

import openai
import matplotlib.pyplot as plt

def prompt(message:str, model="gpt-3.5-turbo"):
    """A prompt helper function that sends a message to openAI
    and returns only the text response.
    """
    client = openai.OpenAI()
    response = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": message}]
    )
    return response.choices[0].message.content

A simple task could be described like in the following. We explictly specify that this should execute from Jupyter to prevent windows popping up.

simple_question = """
Write Python code only and no additional explanatory text.

Write a python program, that 
* loads the file `../../data/blobs.tif`,
* labels objects in this image,
* and draws a mesh between labels with a maximum distance of 50 pixels.

Assume this program would be executed in a Jupyter notebook.
It is not necessary to save the results. Show the results in Jupyter.
"""

The generated code looks like this.

print(prompt(simple_question))
import matplotlib.pyplot as plt
from skimage import io, morphology, measure

# Load the image
image = io.imread('../../data/blobs.tif')

# Threshold the image
thresh = image > 150

# Label objects in the thresholded image
labels = measure.label(thresh)

# Draw a mesh between labels with a maximum distance of 50 pixels
mesh = morphology.dilation(labels) ^ labels

# Show the results
fig, axes = plt.subplots(ncols=2, figsize=(10, 5))
axes[0].imshow(image, cmap='gray')
axes[0].set_title('Original Image')
axes[0].axis('off')
axes[1].imshow(mesh, cmap='gray')
axes[1].set_title('Mesh between labels')
axes[1].axis('off')
plt.show()

Now we provide more details. We explictly state that the code should be like if it was written by a professional. We also give hints for how to solve the image analysis problem.

more_sophisticated_question = """
Please program some python code like a professional would. 
Write Python code only and no additional explanatory text.

Write a python program, that 
* loads the file `../../data/blobs.tif`,
* labels objects using voronoi-otsu-labeling,
* and draws a mesh between labels with a maximum distance of 50 pixels.

I have this code snippet for segmenting an image:
import pyclesperanto_prototype as cle
label_image = cle.voronoi_otsu_labeling(image)

And this is the code snippet for drawing a mesh between objects in a label image:
mesh = cle.draw_mesh_between_proximal_labels(labels, maximum_distance:int)

Assume this program would be executed in a Jupyter notebook.
It is not necessary to save the results. Show the results in Jupyter.
"""
print(prompt(more_sophisticated_question))
import pyclesperanto_prototype as cle
import matplotlib.pyplot as plt

# Load the image
image = cle.imread('../../data/blobs.tif')

# Label the objects using voronoi-otsu-labeling
label_image = cle.voronoi_otsu_labeling(image)

# Draw a mesh between labels with a maximum distance of 50 pixels
mesh = cle.draw_mesh_between_proximal_labels(label_image, maximum_distance=50)

# Display the label image and mesh
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.imshow(cle.imshow(image))
plt.title('Original Image')
plt.axis('off')

plt.subplot(1, 2, 2)
plt.imshow(cle.imshow(mesh))
plt.title('Mesh Between Labels')
plt.axis('off')

plt.show()

Batch prompting#

We now run the same two prompts multiple times, collect the code and test if it executes successfully.

def test_code(question, num_executions = 10):
    """
    Asks chatGPT for code n times and executes it. 
    It returns the number of attempts when it caused an error and the total number of attempts.
    """
    import time
    import random

    num_errors = 0
    for i in range(num_executions):
        print("Attempt", i + 1)
        
        # sleep for a moment to prevent openAI blocking us right away
        sleep_duration = random.uniform(60, 120)
        time.sleep(sleep_duration)
        
        # ask for code
        code = prompt(question)
        
        # clean it a bit
        code = code.replace("```python", "").replace("```", "")

        # execute it
        try:
            exec(code)
        except:
            num_errors += 1
    return num_errors, num_executions
num_errors_simple, num_executions = test_code(simple_question)
Attempt 1
Attempt 2
Attempt 3
Attempt 4
Attempt 5
Attempt 6
Attempt 7
Attempt 8
Attempt 9
../_images/981eddea9eac4ff597668c48920c7ab60527cfa8a8887809e94d0e909d1d8ca1.png ../_images/efef03dcfae7846c5e484c7f5ac3c092e0dcfc5e33195259de282a8b04b11fcc.png ../_images/52a9e038887a6089162f317e4647777fb6693c1aa10286a3fa761d68895ff5f9.png
Attempt 10
num_errors_sophisticated, num_executions = test_code(more_sophisticated_question)
Attempt 1
../_images/8eda7eb7b35e0cbad3880201a17e9b31038a59baa1e179bb96a6d8311c7aa344.png
Attempt 2
../_images/7fe0ad4395e73ce71c24f85284efa592ee6c24ae58215ec12d570f88933df9ba.png ../_images/e8f444b86287361792dacf2be97e867e9d2184c35136887ec9f375f7566e96b2.png ../_images/0f457a2633a2c6a85898bec0e5083a399f599d6142832df8794652d68b9526c6.png
Attempt 3
Attempt 4
../_images/2fd32a0e1cf1e14f446ff237741688840a3c1b5d9275b809c9642a4b7f1a7b0c.png
Attempt 5
Attempt 6
Attempt 7
Attempt 8
../_images/57df3711a9762969261d8561a4d8ecc02d0539b3ba51249d32a60ee7957d1f7f.png
Attempt 9
../_images/ebd173201c84dbca1cce21c2f7a4c1dd7750db9bea414c82ae8d2c47b1be43e6.png ../_images/c2e781666f7b2b06559aa5be29f260789478e205e9fa316b5bd7481ad10d7d31.png
Attempt 10
print("There were ", 
      num_errors_simple, 
      "errors while executing code after asking the simple question for", 
      num_executions, "times.")

print("There were ", 
      num_errors_sophisticated, 
      "errors while executing code after asking the more sophisticated question for", 
      num_executions, "times.")
There were  9 errors while executing code after asking the simple question for 10 times.
There were  5 errors while executing code after asking the more sophisticated question for 10 times.