[PYTHON] Collage automatisch aus Bildliste generieren

Dies ist eine Fortsetzung des letzten und des letzten Males.

Ich habe jedoch eine bessere Methode als "optimale Platzierung mehrerer Bilder" gefunden, um das Optimierungsproblem zu lösen, und habe diese Methode dieses Mal übernommen.

Mit der automatischen Generierung von Collagenvorlagen können Sie nahezu unbegrenzte Vorlagen erstellen. Daher sollten Sie viele Vorlagen erstellen und die auswählen, die die angegebene Bildliste am schönsten anzeigen kann. Eigentlich ist es gut, die Vorlage mit Parametern auszudrücken und das Optimierungsproblem zu lösen, aber das ist eine zukünftige Aufgabe.

Nun müssen wir uns zunächst überlegen, was es bedeutet, das Schönste zeigen zu können. Dieses Mal habe ich beschlossen, die folgenden zwei Punkte hervorzuheben.

  1. Wie kann das Seitenverhältnis beibehalten werden?
  2. Die Streuung der Bildgröße ist gering

Das erste Kriterium ist leicht zu verstehen. Je mehr Sie das Seitenverhältnis des Bildes brechen, desto weniger schön wird es sein. Versuchen Sie also, es beizubehalten. Mit nur dem ersten Kriterium wird jedoch nur ein Bild groß angezeigt, und die anderen Bilder dürfen erheblich klein sein (letztendlich nicht angezeigt). Deshalb haben wir den zweiten Standard übernommen.

Um diese zu realisieren, werden zunächst die folgenden Werte angegeben.

・ $ H $: Vertikale Breite der zu erstellenden Collage ・ $ W $: Breite der zu erstellenden Collage ・ $ \ {x_i \} _ {i = 1} ^ n : n Bilder, die eingebettet werden sollen ( x \ _ i \ in \ mathbb {R} \ _ {++} ^ 2 $ jede vertikale Breite, Repräsentiert die Breite)

Positionieren Sie dann die VorlageL = [\ell\_1 | \cdots | \ell\_n]^T \in \mathbb{R}\_{+}^{n \times 2}Und GrößeS = [s\_1 | \cdots | s\_n]^T \in \mathbb{R}\_{+}^{n \times 2}(In Wirklichkeit sollte die Definition so geschrieben werden, dass sie nicht hervorsteht, aber der Einfachheit halber weggelassen wird.) Die Vorlage wird mit der folgenden Funktion erstellt.

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)

Durch Ändern von random_state für n können verschiedene Vorlagen generiert werden. Versatz ist übrigens der Abstand zwischen den Bildern (Dicke des Randes).

Als nächstes entscheiden Sie, wo jedes Bild in die Vorlage eingefügt werden soll. Dies wird erreicht, indem der Ort mit dem ähnlichsten Seitenverhältnis ausgewählt wird. Suchen Sie den Ort $ j \ _i $ in der Vorlage, an dem das Bild $ i $ unten platziert wird.

\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*}

$ [X] _i $ ist jedoch die $ i $ -Komponente von $ x $. Sobald Sie den Standort ausgewählt haben, können Sie ihn nicht mehr auswählen. Das heißt, $ \ {j \ _1, \ ldots, j \ _n \} = \ {1, \ ldots, n \} $.

Definieren Sie mithilfe der obigen Funktion die Bewertungsfunktion $ f $ für die Vorlagengröße $ S $ wie folgt.

\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*}

Erstellen Sie $ m $ -Vorlagen und nennen Sie sie $ S ^ {(1)}, \ ldots, S ^ {(m)} $. Wählen Sie schließlich eine Vorlage, die $ f $ minimiert.

Ich habe ein Experiment versucht, das auf dem oben genannten basiert. Außerdem habe ich das folgende Bild verwendet.

Die experimentellen Ergebnisse sind wie folgt.

test3.png

Ist es nicht gut eingebettet?

Dieses Mal habe ich versucht, automatisch eine Collage zu generieren. Daher denke ich, dass wir etwas Eigenes gemacht haben. Als zukünftige Ausgabe,

--Parametrisieren und optimieren Sie die Vorlage

Ist es? Der für das Experiment verwendete Code ist unten aufgeführt. Danke fürs Lesen.

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

Collage automatisch aus Bildliste generieren
[Python] Generiert automatisch ein Beispielbild für eine Animationsliste.
Generieren Sie aus Textdaten ein vertikales Bild eines Romans
Extrahierter Text aus dem Bild
Generieren Sie gemeinsam Bildtext
Generieren Sie eine URL-Abfrage aus JSON
Entfernen Sie den Rahmen aus dem Bild