Inventarizace dřeva na pile metodou rozpoznávání objektu v obraze na GIS datech

Magdalena Mračna
9 min readMar 10, 2018

--

Autoři: Magdalena Mračna & Lenka Kravčíková
Mentoři: Ondřej Renner & Tereza Havlíčková (Stora Enso)

Projekt je závěrečnou prací v kurzu Digitální Akademie organizace Czechitas. Vznikl ve spolupráci se společnosti Stora Enso (Ostrava), která poskytla fotografie a odborné poradenství.

Inventarizace dřeva na pile

Během manuální inventarizace dřeva na pilách se pomocí latí ručně měří výška dřevěné kupy. Měření se provádí v intervalech jednoho metru podél kupy. Pracovník skladu se musí při měření pohybovat v bezprostřední blízkosti dřevěných kup, které mohou být nestabilní a vzniká tak riziko úrazu. Získaná data se zapisují do papírového formuláře a z něho následně do excelovské tabulky, ve které se vypočítává kubatura uskladněného dřeva na pile. Inventarizace dřeva probíhá každý měsíc. V tomto čase musí být provoz pily pozastaven, aby se zásoby dřeva na skladě neměnily. Vzhledem k velkému množství kup na pile je zavedený postup inventarizace velmi zdlouhavý a náročný.

Snahou je detekci skladových zásob dřeva na pile zjednodušit a zrychlit. Návrhem je získávání GIS dat a jejich následné vyhodnocení v příslušné aplikaci.

Tento projektu je pouze dílčí části daného návrhu. Obsahem projektu je detekce jednotlivých klád v hromadách na pile a následný výpočet kubatury dřeva. Vzorový kód v Pythonu byl sestaven pro jednu konkrétní fotografii kupy dřevěných klád.

Obr. 1. Fotografie pily

Python, knihovny OpenCV a NumPy

Python je vysokoúrovňový skriptovací programovací jazyk, který je vyvíjen jako open source projekt a zdarma nabízí instalační balíky pro většinu běžných platforem. Díky velkému objemu různých knihoven, Python poskytuje snadno přístupný způsob k řešení dané problematiky. Zde byla zvolena metoda “object detection”, kterou nabízí knihovna OpenCV. Tato knihovna není součástí základního balíčku, proto je potřeba ji před zahájením práce nainstalovat. K instalaci v příkazovém řádku ve Windows slouží příkaz:

python -m pip install opencv-python

Kromě knihovny OpenCV, jsou v kódu použity funkce z knihoven NumPy a Math. Knihovnu NumPy je rovněž potřeba doistalovat, obdobným způsobem. NumPy je základním balíčkem pro vědecké výpočty s Pythonem. Umožňuje definovat libovolné typy dat.

Vytvoření kódu v Python-u

Na začátku se importují potřebné knihovny (popsané výše). Pro knihovnu NumPy je vytvořen alias np. Knihovna OpenCV má označenín cv2. Z knihovny Math se importuje pouze konstanta pi.

import numpy as np
import cv2
from math import pi

Vstupními daty, které jsou vloženy do kódu jsou fotografie hromady dřeva a průměrná délka klád na dané hromadě. Fotografii načteme a upřesníme její datový typ, který bude potřeba později.

img = cv2.imread('pile1.jpg')

Funkce cv2.imread nabízí načtení obrázku jak barevného, tak ve stupních šedi. Tady je fotografie načítaná v barevném spektru BGR.

Následně se obrázek ořeže tak, aby v dalších operacích vznikl co nejmenší počet chybně detekovaných koleček mimo oblast dřevěných klád. Původní rozměry fotografie byly 4608 x 3456 pixelů. Změněná byla výška obrázku, shora se ubralo 850 pixelů a zespodu 876 pixelů. Datový typ výsledného obrázku imgC byl definován jako uint8 (celé číslo od 0 do 255).

imgC = img[850:2580, 0:4608].astype(np.uint8)
Obr. 2. Ořezaná fotografie

První operace, která zpracovává pixely obrázku, se provede pomocí funkce MedianBlur. Tato funkce odstraní z fotografie šum. Převádí medián všech pixelů pod oblasti matice a centrální element je nahrazen touto střední hodnotou. Matice je charakterizována číselným parametrem funkce, který musí být celé liché číslo, aby bylo možné vytvořit čtvercovou matici.

imgB = cv2.medianBlur(imgC, 11)
Obr. 3. Median Blur

Dále se na fotografii zvýrazní červená barva. Dřevo jako materiál má vysokou odrazivost červeného světla, tudíž je na místě posílit červenou složku z BGR barevného spektra. Funkce cv2.split vygeneruje dané tři barvy (B — modrá, G — zelená, R — červená) pro každý pixel na obrázku. V tomto případě byla červená složka zvětšena na 1,5 násobek. Naopak modrá a zelená složka byly zmenšeny na 0,7 násobek.

Funkce cv2.merge znovu sloučí barevné složky dohromady. Opět se kvůli následujícím funkcím definuje datový typ obrázku na uint8.

b, g, r = cv2.split(imgB)
r = 1.5*r
b = 0.7*b
g = 0.7*g
imgR = cv2.merge([b,g,r]).astype(np.uint8)
Obr. 4. Posílení červené barvy

Funkce cv2.Canny je vícestupňový algoritmus. V prvním stupni se odstraní šum z obrazu pomocí Gaussova filtru s matici 5 x 5. Zjemněný obraz je dále filtrován Sobelovou funkci jak ve vertikálním, tak v horizontálním směru. Po získání velikosti a směru gradientu se provádí úplné skenování obrazu, aby se odstranily nežádoucí pixely, které nemusí představovat okraj. U každého pixelu je hledáno lokální maximum. Poté se provede hysteréze prahování. K tomu je potřeba znát dvě prahové hodnoty minVal a maxVal.. Všechny hrany s gradientem intenzity vyšším než maxVal jsou bezpochyby klasifikovány jako okraje. Pokud je hodnota gradientu intenzity pod minVal, není to hrana a hodnota je vyřazena. Gradienty ležící mezi dvěma prahovými hodnotami, jsou započítány mezi hrany pouze pokud jsou spojeny s hranami nad hodnotou maxVal, v opačném případě jsou také vyřazeny. Třetí parametr apertureSize charakterizuje Sobelovu matici.

imgE = cv2.Canny(imgR, 1000, 500, apertureSize=5)
Obr. 5. Canny

Samotná detekce kruhů v obraze, neboli jednotlivých klád na hromadě, se provede pomocí funkce cv2.HoughCircles. K tomu, aby funkce našla správné kruhy na obrázku, potřebuje znát několik argumentů:

  • method — jedinou metodou, kterou lze do funkce implementovat je cv2.HOUGH_GRADIENT, metoda využívá informace o gradientech nalezených hran
  • dp — inverzní poměr rozlišení akumulátoru k rozlišení obrazu, pro dp=2 akumulátor má poloviční hodnotu šířky a výšky
  • minDist — definice minimální vzdálenost středu kružnic
  • param1 —první parametr specifický pro danou metodu, navazuje na předchozí funkci cv2.Canny, koresponduje s jejím parametrem maxVal
  • param2 — druhý parametr specifický pro danou metodu, prahová hodnota akumulátoru pro středy kruhů ve fázi detekce. Čím menší je, tím více falešných kruhů lze zjistit. Kruhy odpovídající velkým hodnotám akumulátoru se vrátí jako první.
  • minRadius — definice nejmenšího poloměru
  • maxRadius — definice největšího poloměru
circles = cv2.HoughCircles(imgE, cv2.HOUGH_GRADIENT, dp=2, minDist=100, param1=1000, param2=50, minRadius=50, maxRadius=80)

Rovnice pro matematicky zápis kružnice je:

kde x(střed) a y(střed) jsou souřadnice středu kružnice a r je její poloměr. Tyto tři parametry kružnice jsou výsledkem, který vrací daná funkce cv2.HoughCircles. Díky ním je možné kružnice vykreslit na fotografii. Použitím funkce np.round se parametry zaokrouhlí na celé číslo a je tedy možné určit datový typ na intiger.

circles = np.round(circles[0, :]).astype("int")

Pro zobrazení výsledných kružnic na fotografii se používají kreslicí funkce z knihovny OpenCV.

Funkci cv2.circle je potřeba definovat střed kružnice (x, y) a její poloměr r. Dále barvu jakou se má kružnice vykreslit (v barevném spektru BGR), zde je zvolená zelená barva (0, 255, 0) a tloušťku čáry, kterou se bude kružnice kreslit (2).

Funkce cv2.rectangle vykreslí na fotografii středy kruhů ve tvaru čtverce. Argumentami funkce jsou horní roh čtverce (x — 1, y — 1) a dolní roh čtverce (x + 1, y + 1). Opět se definuje barva (v BGR), zde budou středy vykresleny červenou barvou (0, 0, 255) a tloušťka čáry. Pokud je hodnota záporná (-1) obarví se nejen okraje čtverce a také jeho vnitřek.

for (x, y, r) in circles:
# drawing circle (green) and its center (red)
cv2.circle(imgC, (x, y), r, (0, 255, 0), 2)
cv2.rectangle(imgC, (x - 1, y - 1), (x + 1, y + 1), (0, 0, 255), -1)
Obr. 6. HoughCircles
Obr. 7. HoughCircles detail

Součástí kódu je vytvoření seznamu poloměrů všech nalezených kružnic. Do prázdného seznamu list_of_radius se postupně pomocí příkazu .append(r) vkládají všechny poloměry. Tato operace probíhá v předchozím cyklu for (x, y, r) in circles: . Na konci cyklu se vypíší nalezené poloměry a jejích celkový počet.

list_of_radius = []
for (x, y, r) in circles:
radius = (list(range(r)))
list_of_radius.append(r)
print("List of detected circles: ", list_of_radius)
print("Count of detected circles: ", len(list_of_radius))

Výpočet kubatury dřeva

Dalším krokem v kódu je výpočet celkového objemu dřeva na hromadě. Na začátku je nutné definovat měrnou jednotku — metr, na kterou bude proveden přepočet z pixelů. V tomto projektu byl jeden metr uměle určen mezi dvěma body (top a bottom) v obrázku. Body byly definovány souřadnicemi x, y. Bod bottom odpovídá zemi. Mezi y-souřadnicemi byl vypočítán rozdíl dif, kterým byly původní velikosti poloměrů (v pixelech) vyděleny, čímž se provedl přepočet na metry.

Obr. 8. Vyznačené body

Dále se vypočítala plocha kruhu surface (ℼ⦁r^2). K výpočtu objemu volume je potřeba průměrná délka klády log_length, zde byla vložená konstanta 4 m přímo do kódu. Po vynásobení plochy délkou se získá objem.

Sčítání ploch a objemu klád se provádí v cyklu for r in circles:, kde na začátku se celková plocha i objem rovnají nule.

sum_surface = 0
sum_volume = 0
for r in circles:
r = radius[-1]
top = 662, 1360
bottom = 660, 1730
dif = bottom[1]-top[1]
r = r/dif
surface = pi * r * r
sum_surface += surface
log_length = 4 # m
volume = surface * log_length
sum_volume += volume
print ("Summary surface of all circles is: ", round(sum_surface), "m2")
print ("Volume of pile is: ", round(sum_volume), "m3"

Po ukončení cyklu se vypíše celková plocha klád v m2 a celkový objem hromady v m3. V editoru PyCharm:

Obr. 9. Výsledek kódu v PyCharm

Výsledek v příkazovém řádku (Windows):

Obr. 10. Výsledek kódu v cmd

Po úspěšném projetí kódu se zobrazí obrázek s detekovanými kruhy v samostatném okně “detected circles”. Program čeká 30 sekund než se okno s obrázkem samo zavře anebo ho uživatel zavře klávesou “Esc”. Pokud uživatel stiskne klávesu “s” výsledný obrázek se uloží ve formátu png.

cv2.imshow('detected circles',imgC)
k = cv2.waitKey(0)
if k == 30: # waiting for key Esc
cv2.destroyAllWindows()
elif k == ord('s'): # waiting for key "s" and save result image
cv2.imwrite('result_pile1.png', imgC)
cv2.destroyAllWindows()

Funkce cv2.imwrite sloužící k uložení obrázku je použitá v kódu za každou funkci. Po spuštění kódu se tedy uloží celkem pět obrázků (cut, mblur, red, canny, result).

Další návrhy na zpracování fotografií

  • Pořízení několika snímků celé hromady dřeva na pile a jejich následné spojení pomocí funkcí v Python-u do jednoho panoramatického obrázku.
    Příslušnými funkcemi se identifikuji klíčové body na každém jednotlivém snímku, další funkcí se naleznou společné klíčové body na dvou sousedících snímcích. Podle společných klíčových bodů se jednotlivé snímky správně transformují (natočí, deformují) tak, aby je bylo možné plynule spojit se sousedními snímky.
    Po vytvoření panoramy by následovalo zpracování obrazu podle výše popsaného kódu.
  • K výpočtu kubatury dřeva je potřeba přesněji definovat jeden metr. Vhodným návrhem je určit výšku jednoho metru ještě před pořízením fotografie. K tomuto účely by mohla sloužit černobílá tabulka (terčík), jejíž střed by byl umístěn ve výšce jednoho metru.Střed tabulky by bylo možné identifikovat Python-em.
  • Kvůli zamezení detekce falešných kruhů mimo hromadu by bylo vhodné definovat oblast, ve které se nacházejí dřevěné klády — linii země a linii kupy dřeva. Toto by definoval sám uživatel v příslušné aplikaci.
  • Posledním krokem by bylo vytvoření aplikace, která by spojila všechny kroky dohromady a uživateli poskytla inventarizační údaje.

Použité zdroje

Instalace knihoven:
http://naucse.python.cz/2018/pyladies-ostrava-jaro/beginners/testing/

Tutoriály ke zpracování obrazu v Python-u:
https://www.pyimagesearch.com/

Hough Circle Transform:
http://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_houghcircles/py_houghcircles.html

Getting started with images:
https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_gui/py_image_display/py_image_display.html

Detecting Circles in Images using OpenCV and Hough Circles:
https://www.pyimagesearch.com/2014/07/21/detecting-circles-images-using-opencv-hough-circles/

An example of the Hough Transform — Pupil Segmentation:
https://craftofcoding.wordpress.com/tag/hough-transform/

Smoothing Images:
https://docs.opencv.org/3.1.0/d4/d13/tutorial_py_filtering.html

Canny Edge Detection:
https://docs.opencv.org/3.3.1/da/d22/tutorial_py_canny.html

OpenCV — Canny Edge Detection:
https://www.tutorialspoint.com/opencv/opencv_canny_edge_detection.htm

--

--