Všechny uzavřené kontury

... Petr Blahoš, 14. 1. 2018 ComputerVision Python

Dosavadní pokusy animovat kontury byly funkční, někdy efektní, ale trochu nepřirozené. V první varantě byla jen vnější kontura, ve druhé variantě se nám zůstávaly viset ve vzduchu čáry. Dnes se posuneme o kousíček dál.
Necháme si všechny kontury, budeme animovat každou zvlášť, a když bude nějaká chybět, tak si některou zduplikujeme. Což je kombinace předchozích dvou postupů.

Animace uzavřených konrut

Pro připomenutí:

  • V první variantě jsme vzali vnější kontury obou obrazců, v kratším jsme rozdělili nejdelší úsečky tak, aby byly kontury stejně dlouhé, a interpolovali jsme mezi nimi.
  • Ve druhé variantě jsme pro oba obrazce vzali všechny kontury, a jejich úsečky jsme dali na jednu hromadu. Pokud byla jedna z hromad menší, tak jsme v ní některé úsečky zduplikovali. Interpolovali jsme mezi úsečkami z těch dvou hromad.

Dnes to zkombinujeme. Vezmeme z obou obrazců všechny kontury. Pokud je v některém z obrazců méně kontur, tak některé kontury zduplikujeme. Seřadíme je podle délky (abychom převáděli delší kontury na delší, a kratší na kratší). No a pak mezi sebou interpolujeme dvojice kontur stejným způsobem, jak v prvním díle.

Směle do toho

Většinu funkcí už máme hotovou z minula. Musíme upravit get_contour tak, aby vrátila všechny kontury (a přejemenujeme ji na get_sorted_shape_list):

def get_sorted_shape_list(img, height, simplified):
    if 3 == len(img.shape):
        img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    thr = cv2.threshold(img, 0, 255,
                        cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
    cnts = cv2.findContours(thr, cv2.RETR_LIST,
                            cv2.CHAIN_APPROX_SIMPLE)[1]
    if simplified:
        cnts = [cv2.approxPolyDP(i, height/200, True) for i in cnts]
    cnts.sort(key=lambda x: cv2.arcLength(x, True), reverse=True)
    return [i for i in cnts if i.shape[0] > 1]

Kromě toho, že vrátí pole kontur seřazených od nejdelší, může kontury zjednodušit (parametr simplified), a vrátí jen ty, které mají více než 1 bod.

Na prodloužení pole kontur použijeme funkci enlarge_line_list z minula, jen je přejmenujeme na správnější extend_list, protože je nezajímá, jestli dostane pole čar, kontur, nebo čehokoliv jiného.

Použijeme i enlarge_contour, kterou známe z prvního dílu.

Když to dáme všechno dohromady, dostaneme asi tak tohle:

def interpolate_shapes(out_shape, i0, i1):
    """
    Interpolates from contours found in i0 to contours found in i1. The size
    of the output image is out_shape.
    """
    SIMPLIFIED = False
    c1l = get_sorted_shape_list(i0, out_shape[1], simplified=SIMPLIFIED)
    c2l = get_sorted_shape_list(i1, out_shape[1], simplified=SIMPLIFIED)

    # make the shapes the same length
    if len(c1l) < len(c2l):
        c1l = extend_list(c1l, len(c2l))
    elif len(c2l) < len(c1l):
        c2l = extend_list(c2l, len(c1l))

    pairs = []
    # now we need all shapes to have the same length
    for i in range(len(c1l)):
        c1 = c1l[i]
        c2 = c2l[i]
        if len(c1) > len(c2):
            c2 = enlarge_contour(c2, len(c1))
        elif len(c2) > len(c1):
            c1 = enlarge_contour(c1, len(c2))
        pairs.append((c1, c2))

    SCALE = 100
    for j in range(SCALE + 1):
        img = np.zeros((out_shape[0], out_shape[1], 3), dtype=np.uint8)
        for (c1, c2) in pairs:
            cf = get_interpolated_points(c1, c2, j, SCALE)
            cv2.drawContours(img, [cf], 0, (255, 0, 255), 4)

        cv2.imshow("A", img)
        cv2.waitKey(1)
    return img
  • Nejprve získáme pole kontur z obou obrázků (get_sorted_shape_list).
  • Potom zařídíme, že budou stejně dlouhé, neboli budou mít stejný počet kontur (extend_list).
  • Dále vytvoříme dvojice kontur - vždy jednu z prvního obrázku a druhou z druhého. V každé dvojici zařídíme, že obě kontury budou stejně dlouhé (enlarge_contour).
  • No a pak už známým způsobem interpolujeme mezi dvojicemi kontur.

Evoluce dopravních prostředků

Závěr

Na závěr Vám, jak je mým zvykem, daruji kompletní kód.

Promo-animace