Všechny uzavřené kontury
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ů.
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.
Závěr
Na závěr Vám, jak je mým zvykem, daruji kompletní kód.
Komentáře byly zrušeny
V EU teď máme složitou situaci s Cookies. Na komentáře jsem používal jistou službu třetí strany. Ta však používá Cookies poměrně, ehm, benevolentně. Tak jsem se rozhodl komentáře zrušit. Pokud chcete, můžete mi napsat přímo