[PYTHON] Optimale Platzierung mehrerer Bilder

Letztes Mal habe ich einen Artikel Automatische Generierung von Collagenvorlagen geschrieben. Collage ist ein Anwendungsbeispiel, und es ist richtiger zu sagen, "eine Methode zur probabilistischen Aufteilung eines Bereichs in n Bereiche".

Diesmal ist es das Gegenteil davon, und wenn n Rechtecke (zum Beispiel Bilder) angegeben werden, ist die Methode, sie schön im Bereich von $ h \ times w $ (im Folgenden als Leinwand bezeichnet) anzuordnen, die richtige Lass uns darüber nachdenken.

Nun werden wir wieder eine probabilistische Methode verwenden. Ein Rechteck kann durch eine gleichmäßige Verteilung dargestellt werden. In der folgenden Abbildung ist beispielsweise die gleichmäßige Verteilung dargestellt, die die Leinwand $ 1 \ times 1 $ darstellt.

collage_uniform_canvas.png

Da die zu platzierenden Rechtecke auch mit einer gleichmäßigen Verteilung ausgedrückt werden können, halte ich es für besser, die gleichmäßige Verteilung, die die Leinwand darstellt, mit der Mischung der gleichmäßigen Verteilung, die die zu platzierenden Rechtecke darstellt, abzugleichen. Divergenz kann verwendet werden, um Dinge wie den Abstand zwischen Wahrscheinlichkeitsdichtefunktionen zu messen. Diesmal wird die KL-Divergenz übernommen. Auf diese Weise wird die Mischung höchstwahrscheinlich geschätzt.

Nun habe ich geschrieben, dass Rechtecke durch eine gleichmäßige Verteilung dargestellt werden können, aber dies ist bei der Optimierung nicht sehr praktisch. Also werde ich versuchen, es mit Gauß zu approximieren. Die zu platzierenden Rechtecke seien $ \ {(h_i, w_i) \} _ {i = 1} ^ n $. Es wird jedoch angenommen, dass $ h_i und w_i $ die vertikale Breite bzw. die horizontale Breite des rechteckigen $ i $ darstellen.

Die Dichtefunktion der Wahrscheinlichkeitsverteilung, die das Rechteck $ i $ darstellt, sei $ N (x; \ mu_i, \ Sigma_i) $ (N bedeutet Normalverteilung). Hier ist $ \ Sigma_i $ wie folgt definiert.

\Sigma_i = \left[\begin{array}{ll}
\frac{h_i}{2} & 0 \\
0 & \frac{w_i}{2}
\end{array}\right]

Diese Verteilung nähert sich nun dem Bild an. Alles was Sie tun müssen, ist $ \ mu_i $ zu schätzen. Sei $ p (x) $ die Wahrscheinlichkeitsdichtefunktion der Verteilung, die die Leinwand darstellt, und $ q (x) = \ sum_ {i = 1} ^ n w_i N (x; \ mu_i, \ Sigma_i) $ für die zu platzierende rechteckige Mischung. , Es sei jedoch $ \ sum_ {i = 1} ^ n w_i = 1 $. Diese Zeitklasse vor ist einheitlich, dh $ w_i = \ frac {1} {n} $.

Nun wird die KL-Divergenz zwischen $ p und q $ wie folgt ausgedrückt.

\begin{align*}
KL(p||q) &= \int p(x) \log \frac{p(x)}{q(x)} dx \\
&\approx - \frac{1}{m} \sum_{j=1}^m \log q(x_j)
\end{align*}

Schließlich wurde die Stichprobe als $ x_j \ sim p (x) $ angenähert. Dies wird minimiert, indem die Einschränkung auferlegt wird, dass das Rechteck nicht hervorsteht und nicht abdeckt. Verwenden Sie zur Optimierung scipy.optimize.minimize. Möglicherweise projizierter Gefälleabstieg.

Die experimentellen Ergebnisse sind wie folgt. Die erste Figur ist ein richtig angeordnetes Rechteck, und ich werde mein Bestes tun, um es zu reproduzieren. Die folgende Abbildung zeigt das Platzierungsergebnis. Sie können sehen, dass es gut angeordnet ist.

Beim nächsten Mal möchte ich in der Vorlagengenerierung des vorherigen Artikels Vorlagen generieren und einen Hochgeschwindigkeitsabgleich mit einer bestimmten Bildliste implementieren.

collage2.png

collage3.png

import itertools
import matplotlib.pyplot as plt
import numpy as np
from scipy import stats, optimize

def generate_template(n, width, height, random_state=1, max_random_state=10000, 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 random_state_list in 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]
            #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]
            #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 L


def negative_log_likelihood(L, X, Z):
    n_components = X.shape[0]
    L = L.reshape((n_components, 2))
    mixture_rate = np.repeat(1/n_components, n_components)
    values = np.zeros((Z.shape[0], n_components))
    for i in range(n_components):
        q = stats.multivariate_normal(L[i]+X[i]/2, [[X[i][0]/2, 0], [0, X[i][1]/2]])
        values[:, i] = q.pdf(Z) * mixture_rate[i]
    tmp = values.sum(axis=1)
    return - np.log(tmp[tmp!=0]).sum()


def compute_intersected_area(p1, p2, x1, x2):
    left = np.max([p1[0], p2[0]])
    right = np.min([p1[0] + x1[0], p2[0] + x2[0]])
    bottom = np.max([p1[1] + x1[1], p2[1] + x2[1]])
    top = np.min([p1[1], p2[1]])
    return np.max([0, (right - left) * (bottom - top)])


def constraint(L, X):
    P = L.reshape(X.shape)
    intersected_area = 0.0
    for i, j in itertools.combinations(range(X.shape[0]), 2):
        #intersected_area += compute_intersected_area(P[i], P[j], X[i]*s[i], X[j]*s[j])
        intersected_area += compute_intersected_area(P[i], P[j], X[i], X[j])
    return intersected_area


def estimate(X, w, h, scale=2, random_state=1):
    r = np.random.RandomState(random_state)
    Ls = r.uniform(0, 1, size=(20, X.shape[0]*2))

    px = stats.uniform(0, 1)
    py = stats.uniform(0, 1)
    pxy = lambda xy: px.pdf(xy[:, 0]) * py.pdf(xy[:, 1])

    n_samples = 500

    Z = np.c_[px.rvs(n_samples, random_state=rs), py.rvs(n_samples, random_state=rs+1)]

    constraints = ({"type": "eq", "fun": constraint, "args": (X,)})
    #bounds = [tmp for i in range(X.shape[0]) for tmp in [(0, scale-X[i][0]), (0, scale-X[i][1])]]
    bounds = [tmp for i in range(X.shape[0]) for tmp in [(0, w*scale-X[i][0]), (0, h*scale-X[i][1])]]
    lowest_loss = np.inf
    best_result = None
    for L in Ls:
        result = optimize.minimize(
            negative_log_likelihood,
            L.ravel(),
            args=(X, Z),
            bounds=bounds,
            options={"disp": True, "maxiter": 500},
            constraints=constraints,
        )
        if result.status != 0:
            continue
        loss = negative_log_likelihood(result.x.reshape(X.shape), X, Z)
        if loss < lowest_loss:
            best_result = result
            lowest_loss = loss
    result = best_result
    return result.x.reshape(X.shape)


def plot_result(L_pred, X, n_samples=100, colors=["b", "g", "r", "c", "m", "y", "k"]):
    n = L_pred.shape[0]
    for i in range(n):
        Z_i = np.c_[stats.uniform.rvs(L_pred[i][0], X[i][0], size=n_samples),
                    stats.uniform.rvs(L_pred[i][1], X[i][1], size=n_samples)]
        print(Z_i.max())
        plt.scatter(Z_i[:, 0], Z_i[:, 1], c=colors[i])
    plt.show()

rs = 1
width, height = 1, 1

L = generate_template(2, width, height, random_state=3, offset=0)
L = np.array(L)
X = np.c_[L[:, 2] - L[:, 0], L[:, 3] - L[:, 1]]


L_pred = estimate(X, width, height, scale=2)

tmp = np.maximum((L_pred + X).max(axis=0), 1)
plot_result(L_pred / tmp, X / tmp, n_samples=500)

Recommended Posts

Optimale Platzierung mehrerer Bilder
Mehrfachvererbung von Klassen
Kopie mehrerer Listen
So zeigen Sie mehrere Bilder einer Galaxie in Kacheln an
Summe mehrerer Numpy-Arrays (Summe)
Fangen Sie mehrere Arten von Ausnahmen ab
Liste der selbst erstellten Docker-Bilder
Installieren Sie mehrere Versionen von Python
Beschleunigen Sie das Laden von Python-Bildern
[ev3dev × Python] Steuerung mehrerer Motoren
Bildpixel-Manipulation in Python
Verschlüsseln Sie das Bild beim Drücken fälschlicherweise
Umbenennung der Seriennummer von verkratzten Bildern
Veröffentlichen Sie mehrere Twitter-Bilder mit Python
Animieren Sie mehrere Standbilder mit Python
Über die Farbhalbtonverarbeitung von Bildern