[PYTHON] Générer automatiquement un collage à partir de la liste d'images

C'est une continuation de la dernière fois et de la dernière fois.

Cependant, j'ai trouvé une meilleure méthode que le "placement optimal de plusieurs images" pour résoudre le problème d'optimisation, j'ai donc adopté cette méthode cette fois.

Avec la génération automatique de modèles de collage, vous pouvez créer des modèles presque illimités. Par conséquent, vous devez créer de nombreux modèles et sélectionner celui qui peut afficher le plus joliment la liste d'images donnée. En fait, il est bon d'exprimer le modèle avec des paramètres et de résoudre le problème d'optimisation, mais c'est une tâche future.

Maintenant, tout d'abord, nous devons réfléchir à ce que cela signifie pour pouvoir afficher le plus beau. Cette fois, j'ai décidé de souligner les deux points suivants.

  1. Comment maintenir le rapport hauteur / largeur?
  2. La dispersion de la taille de l'image est faible

Le premier critère est facile à comprendre. Plus vous cassez le rapport hauteur / largeur de l'image, moins elle sera belle, alors essayez de la conserver. Cependant, avec seulement le premier critère, une seule image est affichée en grand, et les autres images peuvent être considérablement petites (finalement non affichées). Par conséquent, nous avons adopté la deuxième norme.

Quant à la manière de les réaliser, tout d'abord, les valeurs suivantes sont données.

・ $ H $: Largeur verticale du collage à créer ・ $ W $: Largeur du collage à créer ・ $ \ {x_i \} _ {i = 1} ^ n : n images à intégrer ( x \ _ i \ in \ mathbb {R} \ _ {++} ^ 2 $ chaque largeur verticale, Représente la largeur)

Puis positionnez le gabaritL = [\ell\_1 | \cdots | \ell\_n]^T \in \mathbb{R}\_{+}^{n \times 2}Et tailleS = [s\_1 | \cdots | s\_n]^T \in \mathbb{R}\_{+}^{n \times 2}(En réalité, la définition doit être écrite de manière à ne pas dépasser, mais elle est omise par souci de simplicité). Le modèle est créé avec la fonction suivante.

def generate_template(n, width, height, random_state=1, max_random_state=1000000, offset=0):
    L = [np.array([offset, offset, width-offset, height-offset])]
    random_state_lists = stats.randint.rvs(0, max_random_state, size=(n-1, 4), random_state=random_state)

    for idx, random_state_list in enumerate(random_state_lists):
        n_areas = len(L)
        if n_areas == 1:
            i = 0
        else:
            p = np.repeat(1 / (n_areas + i), n_areas)
            x = stats.multinomial.rvs(1, p, size=1, random_state=random_state_list[0])[0]
            i = x.argmax()

        y = stats.bernoulli.rvs(0.5, size=1, random_state=random_state_list[1])[0]
        if y == 0:
            b = stats.uniform.rvs(L[i][0], L[i][2] - L[i][0], size=1, random_state=random_state_list[2])[0]
        else:
            b = stats.uniform.rvs(L[i][1], L[i][3] - L[i][1], size=1, random_state=random_state_list[3])[0]
        if y == 0:
            area1 = np.array([L[i][0], L[i][1], b-offset/2, L[i][3]])
            area2 = np.array([b+offset/2, L[i][1], L[i][2], L[i][3]])
        else:
            area1 = np.array([L[i][0], L[i][1], L[i][2], b-offset/2])
            area2 = np.array([L[i][0], b+offset/2, L[i][2], L[i][3]])
        L.pop(i)
        L.append(area1)
        L.append(area2)
    return np.array(L)

Différents modèles peuvent être générés en modifiant random_state pour n. Au fait, le décalage est la distance entre les images (épaisseur de la bordure).

Ensuite, décidez où placer chaque image dans le modèle. Ceci est réalisé en choisissant l'emplacement avec le rapport hauteur / largeur le plus similaire. Trouvez l'emplacement $ j \ _i $ dans le modèle où l'image $ i $ sera placée ci-dessous.

\begin{align*}
j_i &= \text{argmin}_{j} \ \|r_i - r'_j \|^2 \\
r_i &= \frac{[x_i]_1}{[x_i]_0}, \ r'_j = \frac{[s_j]_1}{[s_j]_0}
\end{align*}

Cependant, $ [x] _i $ est le composant $ i $ de $ x $. De plus, une fois que vous avez sélectionné l'emplacement, vous ne pouvez pas le sélectionner. Autrement dit, $ \ {j \ _1, \ ldots, j \ _n \} = \ {1, \ ldots, n \} $.

En utilisant ce qui précède, définissez la fonction de score $ f $ pour la taille du modèle $ S $ comme suit.

\begin{align*}
f(S) = \sum_{i=1}^n \|r_i - r_{j_i}\|^2 + \frac{\alpha}{n}
 \sum_{i=1}^n \left( [s_i]_1 [s_i]_2 - \frac{1}{n} \sum_{i'=1}^n [s_{i'}]_1 [s_{i'}]_2 \right)^2
\end{align*}

Créez des modèles $ m $ et appelez-les $ S ^ {(1)}, \ ldots, S ^ {(m)} $. Enfin, choisissez un modèle qui minimise $ f $.

J'ai essayé une expérience basée sur ce qui précède. De plus, j'ai utilisé l'image suivante.

Les résultats expérimentaux sont les suivants.

test3.png

N'est-il pas bien intégré?

Cette fois, j'ai essayé de générer automatiquement un collage. En conséquence, je pense que nous avons fait quelque chose de propre. En tant que futur numéro,

C'est ça? Le code utilisé pour l'expérience est répertorié ci-dessous. Merci pour la lecture.

import itertools
import glob
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
from skimage import io, transform


def generate_template(n, width, height, random_state=1, max_random_state=1000000, offset=0):
    L = [np.array([offset, offset, width-offset, height-offset])]
    random_state_lists = stats.randint.rvs(0, max_random_state, size=(n-1, 4), random_state=random_state)

    for idx, random_state_list in enumerate(random_state_lists):
        n_areas = len(L)
        if n_areas == 1:
            i = 0
        else:
            p = np.repeat(1 / (n_areas + i), n_areas)
            x = stats.multinomial.rvs(1, p, size=1, random_state=random_state_list[0])[0]
            i = x.argmax()

        y = stats.bernoulli.rvs(0.5, size=1, random_state=random_state_list[1])[0]
        if y == 0:
            b = stats.uniform.rvs(L[i][0], L[i][2] - L[i][0], size=1, random_state=random_state_list[2])[0]
        else:
            b = stats.uniform.rvs(L[i][1], L[i][3] - L[i][1], size=1, random_state=random_state_list[3])[0]
        if y == 0:
            area1 = np.array([L[i][0], L[i][1], b-offset/2, L[i][3]])
            area2 = np.array([b+offset/2, L[i][1], L[i][2], L[i][3]])
        else:
            area1 = np.array([L[i][0], L[i][1], L[i][2], b-offset/2])
            area2 = np.array([L[i][0], b+offset/2, L[i][2], L[i][3]])
        L.pop(i)
        L.append(area1)
        L.append(area2)
    return np.array(L)


def estimate(X, w, h, random_states, offset=0):
    r = np.c_[X[:, 0] / X[:, 1]]
    r2 = r**2
    best_L = None
    best_s = None
    lowest_linkage = np.inf
    for random_state in random_states:
        s = stats.uniform.rvs(0, 1, random_state=random_state)
        L = generate_template(X.shape[0], w*s, h*s, random_state=random_state, offset=offset*s)
        r_temp = np.c_[(L[:, 2] - L[:, 0]) / (L[:, 3] - L[:, 1])]
        r_temp2 = r_temp**2
        dist2 = r2 + r_temp2.T - r.dot(r_temp.T)

        assignment = []
        linkage = 0
        selected = set()
        for i, idx in enumerate(dist2.argsort(axis=1)):
            for j in idx:
                if j not in selected:
                    linkage += dist2[i, j]
                    assignment.append(j)
                    selected.add(j)
                    break
        assignment = np.array(assignment)
        L = L[assignment]
        A = L.copy()
        size = np.c_[L[:, 2] - L[:, 0], L[:, 3] - L[:, 1]]
        L = np.c_[L[:, 0], L[:, 1], np.min(size / X, axis=1)]
        mu = L[:, 2].mean()
        var = np.sqrt(np.mean((L[:, 2] - mu)**2))
        linkage = linkage + var
        if linkage < lowest_linkage:
            lowest_linkage = linkage
            best_L = A
            best_s = s
    return best_L, best_s, lowest_linkage


if __name__ == '__main__':
    images = []
    X = []
    for filename in glob.glob("img/*")[:3]:
        image = io.imread(filename)
        images.append(image)
        X.append((image.shape[0], image.shape[1]))
    X = np.array(X)

    Z = np.c_[X[:, 1], X[:, 0]]

    width, height = 375, 667
    L, s, linkage_value = estimate(Z, width, height, range(100), offset=0)
    L = L / s
    print(linkage_value)

    position = np.int32(np.c_[L[:, 1], L[:, 0]])
    size = np.int32(np.c_[L[:, 3] - L[:, 1], L[:, 2] - L[:, 0]])
    
    canvas = np.zeros((height, width, 3))
    for i in range(len(images)):
        image = images[i]
        print(image.shape)
        canvas[position[i][0]:position[i][0]+size[i][0], position[i][1]:position[i][1]+size[i][1]] = transform.resize(image, size[i])    

    plt.imshow(canvas)
    plt.show()

Recommended Posts

Générer automatiquement un collage à partir de la liste d'images
[Python] Génère automatiquement un exemple d'image de type liste d'animation.
Générer une image verticale d'un roman à partir de données textuelles
Texte extrait de l'image
Générer du texte d'image ensemble
Générer une requête URL à partir de JSON
Supprimer le cadre de l'image