Search code examples
pythonopencvimage-processingvideo-processingwebcam

Creating a 'drift/morph/melt' effect on webcam footage using python and opencv


I am currently working on a school project where I am trying to simulate an LSD trip using a webcam and video processing effects. I am using python and opencv to accomplish this, but I am having trouble figuring out how to create/apply a certain effect that acts like a "drift/morph/melt/flow" to the webcam footage.

I have attached an example of the effect I am trying to achieve. It looks like the image is slowly melting and distorting, almost as if it is being pulled in multiple directions at once.

I have looked into various image processing techniques such as warping, affine transformations, and image blending, but I am not sure which method would be best for creating this specific effect. Below are some of the lines of code I have tried (I am very new to coding so I have just been playing around with stuff I have already found made on the internet):

import cv2
import numpy as np

# Capture video from webcam
cap = cv2.VideoCapture(0)

while True:
    # Read frame from webcam
    ret, frame = cap.read()

    # Apply swirling effect
    rows, cols = frame.shape[:2]
    for i in range(rows):
        for j in range(cols):
            dx = i - rows // 2
            dy = j - cols // 2
            distance = np.sqrt(dx**2 + dy**2)
            angle = np.arctan2(dy, dx) + distance * 0.1
            x = int(rows // 2 + distance * np.cos(angle))
            y = int(cols // 2 + distance * np.sin(angle))
            if x >= 0 and x < rows and y >= 0 and y < cols:
                frame[i, j] = frame[x, y]

    # Display the resulting frame
    cv2.imshow('Video', frame)

    # Break the loop if the user hits 'q'
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# Release the capture and destroy the window
cap.release()
cv2.destroyAllWindows()

import cv2
from skimage.transform import swirl

# Create a VideoCapture object to access the webcam
cap = cv2.VideoCapture(0)

while True:
    # Read a frame from the webcam
    _, frame = cap.read()

    # Convert the frame to grayscale
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # Apply the swirl effect to the frame
    swirled = swirl(gray, rotation=0, strength=10, radius=120)

    # Display the swirled frame in a window
    cv2.imshow('Swirled', swirled)

    # Wait for the user to press a key
    key = cv2.waitKey(1) & 0xFF
    if key == ord('q'):
        break

# Release the VideoCapture object and destroy all windows
cap.release()
cv2.destroyAllWindows()

I have also found a link with someone achieved a similar effect to what I am looking for using a program called TouchDesigner

Any advice or guidance on how to accomplish this using python and opencv and any other libraries I might need would be greatly appreciated.


Solution

  • Here is one way to create an animated GIF from one single image (or video frame) in Python/OpenCV/PIL.

    • Read the input image
    • Set parameters
    • Create X and Y ramps
    • Loop over the input creating sinusoids and incrementing the phase
    • Use remap to warp the input according to the sinusoids in X and Y
    • Convert the image to PIL format and save the frames in a list
    • When loop is finished, save the frames from the list to an animated GIF using PIL

    Input:

    enter image description here

    import numpy as np
    import cv2
    from PIL import Image
       
    img = cv2.imread("bluecar_sm.jpg")
    
    # get dimensions
    h, w = img.shape[:2]
    
    # set wavelength
    wave_x = 2*w
    wave_y = h
    
    # set amount, number of frames and delay
    amount_x = 10
    amount_y = 5
    num_frames = 100
    delay = 50
    border_color = (128,128,128)
    
    # create X and Y ramps
    x = np.arange(w, dtype=np.float32)
    y = np.arange(h, dtype=np.float32)
    
    frames = []
    # loop and change phase
    for i in range(0,num_frames):
    
        # compute phase to increment over 360 degree for number of frames specified so makes full cycle
        phase_x = i*360/num_frames
        phase_y = phase_x
    
        # create sinusoids in X and Y, add to ramps and tile out to fill to size of image
        x_sin = amount_x * np.sin(2 * np.pi * (x/wave_x + phase_x/360)) + x
        map_x = np.tile(x_sin, (h,1))
    
        y_sin = amount_y * np.sin(2 * np.pi * (y/wave_y + phase_y/360)) + y
        map_y = np.tile(y_sin, (w,1)).transpose()
    
        # do the warping using remap
        result = cv2.remap(img.copy(), map_x, map_y, cv2.INTER_CUBIC, borderMode = cv2.BORDER_CONSTANT, borderValue=border_color)
            
        # show result
        cv2.imshow('result', result)
        cv2.waitKey(delay)
    
        # convert to PIL format and save frames
        result = cv2.cvtColor(result, cv2.COLOR_BGR2RGB)
        pil_result = Image.fromarray(result)
        frames.append(pil_result)
    
    # write animated gif from frames using PIL
    frames[0].save('bluecar_sm_animation.gif',save_all=True, append_images=frames[1:], optimize=False, duration=delay, loop=0)
    

    Full Sized Animated GIF

    Here is a reduced version that has a small enough file size so that it can be displayed directly.

    enter image description here