Search code examples
pythonjupyter-notebookpython-imaging-librarypixelipycanvas

how to draw a pixel in ipycanvas


I cannot figure out how to draw a pixel in ipycanvas. I am drawing rectangles instead of pixels and this makes drawing very slow.

Drawing a rectangle using:

canvas.fill_rect

Code to display image in ipycanvas :

import pandas as pd
import numpy as np

import matplotlib.pyplot as plt

from PIL import Image

import ipycanvas
from ipycanvas import Canvas

import requests
from io import BytesIO

url = r"https://wallpapercave.com/dwp1x/wp1816238.jpg"
response = requests.get(url)
img = Image.open(BytesIO(response.content))

array = img.tobytes()

canvas = Canvas(width=img.width, height=img.height)
with ipycanvas.hold_canvas():
    for i in range(int(len(array)/3)):
        r = array[i * 3 + 0] # red
        g = array[i * 3 + 1] # green
        b = array[i * 3 + 2] # blue
        canvas.fill_style = f"#{r:02x}{g:02x}{b:02x}" # setting color
        canvas.fill_rect(i%img.width, int(i/img.width), 1, 1) # drawing rectangle
canvas

Output:

output image

I am drawing image pixel by pixel because I want to apply filters in images.

How to draw pixels in ipycanvas?


Solution

  • Not sure if this will help but given you're talking about filtering I'd assume you mean things like convolutions. Numpy and Scipy help a lot and provide various ways of applying these and work well with images from Pillow.

    For example:

    import requests
    from io import BytesIO
    from PIL import Image
    
    import numpy as np
    from scipy import signal
    
    image_req = requests.get("https://wallpapercave.com/dwp1x/wp1816238.jpg")
    image_req.raise_for_status()
    
    image = Image.open(BytesIO(image_req.content))
    
    # create gaussian glur of a given standard deviation
    sd = 3
    filt = np.outer(*2*[signal.windows.gaussian(int(sd*5)|1, sd)])
    filt /= filt.sum()
    
    # interpret image as 3d array
    arr = np.array(image)
    
    # apply it to each channel independently, this loop runs in ~0.1 seconds
    for chan in range(3):
        arr[:,:,chan] = signal.oaconvolve(arr[:,:,chan], filt, mode='same')
    
    # array back into image for display in notebook
    Image.fromarray(arr)
    

    This produces an image like:

    blurred image