Outils pour utilisateurs

Outils du site


diy:projets:tiltshift

Introduction

Le Tilt Shift est un effet trompe-l’œil. Plus spécifiquement, il consiste à donner l'impression à l’œil qu'une photo prise à grande distance a en réalité été prise de très près. Cela donne ainsi l'impression de regarder une maquette plutôt qu'un véritable paysage.

Pour se faire, on va artificiellement réduire la profondeur en floutant les parties inférieures et supérieures de l'image (de manière très générale, plus un objet observé est proche, plus la profondeur de champ est faible).
Pour obtenir l'effet recherché, le flou doit être progressif. On ne doit pas avoir de coupure nette entre la zone nette et la zone floutée.
Qui plus est, du fait de son fonctionnement, le Tilt Shift ne donnera l'effet escompté que si l'image originale a été prise à grande distance. Cela n'est pas vraiment possible avec un Raspberry Pi. Il vaut mieux utiliser une caméra montée sur un drone ou installée sur un toit.

Cet effet peut aussi être appliqué à une vidéo, ce que nous ne ferons pas ici par manque de vidéo exploitable.

Explication

Le programme procède en 3 parties : on applique un flou, puis on génère un masque, et enfin on applique le masque pour fusionner l'image floutée et l'image originale.

Pour le flou, plutôt que d'appliquer un simple flou Gaussien, on va tenter de mieux répliquer l'effet produit par une caméra en appliquant d'abord un filtre bilatéral pour réduire le bruit avant d'appliquer un flou simple. On augmente aussi légèrement la valeur et la saturation de l'image.

def blur(image):
    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    hue, saturation, value = cv2.split(hsv)
 
    saturation = np.array(saturation * 1.2, dtype=np.uint16)
    saturation = np.array(np.clip(saturation, 0, 255), dtype=np.uint8)
 
    value = np.array(value * 1.1, dtype=np.uint16)
    value = np.array(np.clip(value, 0, 255), dtype=np.uint8)
 
    image = cv2.cvtColor(cv2.merge((hue, saturation, value)), cv2.COLOR_HSV2BGR)
 
    image = cv2.bilateralFilter(image, 9, 150, 150)
    image = cv2.blur(image, (15, 15))
    return image

Pour appliquer le masque, on le parcourt simplement pixel par pixel et on se sert du niveau de gris comme coefficient pour mélanger les couleurs sur l'original et de la version floutée.

def blend(original, blurred, mask):
    height, width, channels = original.shape
    output = np.zeros((height, width, 3), np.uint8)
    for i in range(0, height):
        for j in range(0, width):
            alpha = mask.item(i, j) / 255
            b_value = original.item(i, j, 0) * alpha + blurred.item(i, j, 0) * (1 - alpha)
            g_value = original.item(i, j, 1) * alpha + blurred.item(i, j, 1) * (1 - alpha)
            r_value = original.item(i, j, 2) * alpha + blurred.item(i, j, 2) * (1 - alpha)
            output.itemset((i, j, 0), b_value)
            output.itemset((i, j, 1), g_value)
            output.itemset((i, j, 2), r_value)
    return output

On se sert des deux fonctions précédentes pour obtenir l'effet désiré : D'abord, on lit l'image en entrée, puis on applique la fonction permettant de la flouter pour obtenir une seconde image.
On génère ensuite un masque simple. On créé une image noire de taille adaptée, on rajoute une bande blanche au centre, puis on applique un flou sur le résultat pour obtenir un dégradé. On peut maintenant fusionner les deux images en se servant de la fonction blend décrite précédemment.

if (len(sys.argv) == 2):
    pic = cv2.imread(sys.argv[1])
 
    b_pic = blur(pic)
 
    height, width, channels = pic.shape
    mask = np.zeros((height, width, 1), float)
    mask[0.4*height:0.6*height] = 255
    mask = cv2.blur(mask, (150, 150))
    shifted = blend(pic, b_pic, mask)
    cv2.imwrite('tshift.jpg', shifted)
diy/projets/tiltshift.txt · Dernière modification : 2018/05/28 09:33 de ycaminade