Skip to main content

Software pro rozpoznání jednotlivých zvířat v chovné hale na základě obrazových dat

  • Název výstupu:

    Software pro rozpoznání jednotlivých zvířat v chovné hale na základě obrazových dat

  • Popis výstupu:

    Software automaticky detekuje zvířata (výkrmový brojleři) na základě získaných obrazových dat v prostoru chovné haly. Využívá principů strojového vidění a učení. Pravděpodobnost úspěšné detekce je minimálně 90 %.

  • Klíčová slova:
    Zemědělství 4.0; precizní zemědělství; strojové vidění; chov drůbeže
  • Dosažení výstupu:

    06/2024

  • Autoři:

    Petr Bartoš, Roman Bumbálek, Radim Kuneš, Luboš Smutný, Jean de Dieu Marcel Ufitikirezi, Tomáš Zoubek

  • Vlastníci:
    Jihočeská univerzita v Českých Budějovicích, IČ: 60076658, podíl na výsledku: 50 %.
    AGROSOFT Tábor, s.r.o., IČ: 25169165, podíl na výsledku: 50 %.
  • Kód důvěrnosti:
    C = podléhá obchodnímu tajemství

Popis aplikace

V rámci projektu TM04000023 byl vytvořen Software pro rozpoznávání zvířat v chovné hale na základě obrazových dat. Software je natolik robustní, že dokáže spolehlivě s pravděpodobností minimálně 90 % detekovat chované brojlery ve výkrmové hale. Na obrázcích 1–3 jsou uvedeny výstupy z uvedeného softwaru, pro rozpoznání chovaných brojlerů. Uvedené podklady reprezentují chov v různých fázích výkrmu a demonstrují spolehlivost software při detekování brojlerů z datových vstupů, které nesou známky šumu (zvíře je z části zakryté chovnou technologií, zvířata se navzájem překrývají, apod.).

Obrázek 1: Začátek výkrmového cyklu

Obrázek 2: Pokročilá fáze výkrmu

Obrázek 3: Finální fáze výkrmu

Popis použitého programovacího jazyka a doplňujících balíků

Pro vytvoření softwaru k systému analýzy obrazu umožňující automatizované hodnocení welfare chovu byl využit interpretovaný programovací jazyk Python 3.9 společně s následujícími knihovnami, které byly využity pro své specifické vlastnosti a funkce potřebné k vytvoření výsledného softwaru:

  • numpy 1.23.5
  • torch 1.13.1
  • cv2 1.0.0
  • datetime 4.9
  • multiprocessing 2.6.2.1
  • psycopg2 2.9.5
  • matplotlib 3.6.3

Balík NumPy je základním nástrojem pro provádění vědeckých výpočtů v jazyce Python. Mezi jeho hlavní funkcí je poskytování možností využití N-rozměrných polí, sofistikovaných funkcí, funkce lineární algebry, Fourierovy transformace a náhodných čísel.

Balík PyTorch umožňuje implementaci metod hlubokého učení v rámci Pythonu včetně výpočtů na bázi tenzorů poskytujících silnou akceleraci na GPU.

OpenCV je knihovna s otevřeným zdrojovým kódem, která obsahuje široké spektrum konvenčních algoritmů zpracování obrazu.

Balík DateTime usnadňuje práci s časovými formáty a umožňuje využití speciálních funkcí pro jejich úpravu a zpracování.

Knihovna Multiprocessing podporuje paralelní souběh procesů pomocí API podobného modulu Threading. Je schopen místo vláken využívat podprocesy, čímž je schopen umožnit lokální i vzdálenou souběžnost procesů.

Psycopg je implementací nejpoužívanějšího, spolehlivého a funkčně bohatého adaptéru PostgreSQL pro Python.

Matplotlib je komplexní knihovna pro vytváření statických, animovaných a interaktivních vizualizací v jazyce Python

Popis vytrénovaného modelu

Pro vytrénování modelu sloužícího ke stanovení hmotnosti zvířete byla využita architektura konvoluční neuronové sítě YOLO. V rámci trénovacího procesu bylo nutné optimalizovat hyperparametry Learning rate, Batch size a Optimizér.

Learning rate (LR) je hyperparametr optimizéru, který určuje velikost změny v každé iteraci, tj. jak rychle se posunujeme ve směru lokálního optima. Z praktického pohledu na kvalitu nalezených výsledků, příliš velké LR způsobí tzv. overfitting, tj. model se příliš rychle specializuje na nalezené lokální optimum, a nedosáhne optima globálního. Což vede v konečném důsledku k horším dosaženým výsledkům. Příliš malé LR naopak vede k velmi pomalému procesu trénování, tedy v přiděleném čase nemusí konvergovat do optima.

Batch size (BS) je velikost mini batch v každém kroku trénovaní. BS ovlivňuje trénování následujícím způsobem. Větší BS znamená vetší nároky na paměť GPU během trénování, vede k lineárnímu zrychlení trénování a dále velikost BS ovlivňuje stabilitu konvergence, a tedy nalezeného řešení.

Optimizér je z obecného pohledu postup, který říká, jak se v modelu při backpropagaci mají upravit váhy. Z praktického hlediska výběr vhodného optimizéru ovlivňuje jak kvalitu výsledného nalezeného řešení (konvergenci), tak dobu trénovaní.

Na základě porovnání vybraných metrik (Precision, Recall, mAP_0.5 a mAP_0.5:0.95) došlo k nalezení ideálních nastavení výše zmíněných hyperparametrů na hodnoty uvedené v tabulce 1.

Tabulka 1: Zjištěné optimální hodnoty hyperparametrů použitých pro vytrénování modelu

HyperparametrZískaná hodnota
Learning rate

0,01

Batch size

48

Optimizér

SGD

Schopnost klasifikace vytrénovaného modelu je znázorněna pomocí matice záměn predikcí vyobrazené na obrázku 4, kde je zaznamenán počet případů, kdy nedošlo k detekci kuřat na obrazcích a byly tak vyhodnoceny jako pozadí. Na obrázku 5 je vizualizován průběh pozorovaných metrik během procesu trénování detekčního modelu. V průběhu všech 300 epoch se získané hodnoty všech metrik zlepšovaly, čímž se zpřesňoval výsledný vytrénovaný model. Predikce detekce sledovaných objektů vytrénovaným modelem konvoluční neuronové sítě architektury YOLO je představena na obrázku 6. 

Obrázek 4: Matice záměn predikcí pro vytrénovaný model CNN s architekturou YOLO

Obrázek 5: Vizualizace průběhu metrik sledovaných v průběhu trénovacího procesu

Obrázek 6: Predikce polygonální masek sledovaných objektů vytrénovaným modelem

Doporučené hardwarové požadavky na přetrénování modelu

Pro nasazení v rozdílných stájových prostředích je doporučen nejprve testovací provoz spojený se sběrem dat, která jsou následně použita k přetrénování původního modelu, čímž dojde k zpřesnění predikce definovaných tříd. Samotné přetrénování modelu pro konkrétní podmínky provozu probíhá na výkonném GPU serveru, pro následnou aplikaci SW v reálném provozu zahrnující provedení inferencí vedoucí k získání detekcí požadovaných objektů již není potřeba tak velké množství výpočetního výkonu, avšak nároky nelze generalizovat, jelikož se odvíjí od počtu kamer snímající vstupní data, požadované frekvence prováděného vyhodnocení i složitosti implementovaného CNN modelu. Pro analýzu obrazu ze dvou kamer o frekvenci jednou za sekundu lze doporučit výpočetní stanici obsahující GPU s níže uvedenými minimálními hardwarovými parametry.

Tabulka 2: Doporučené minimální hardwarové parametry GPU

ParametrHodnota
Velikost paměti

6 GB

Typ pamětí

GDDR6/HBM

Propustnost paměti

360 GB/s

Počet CUDA jader

3 500

Teoretický výkon v FP32

12 TFLOPS

Příloha

Přílohou dokumentace softwarového řešení je také zdrojový kód. Z důvodu ochrany know-how řešitelů jsou uvedeny pouze určité pasáže.

class Camera:
	def __init__(self, IP, inDir, fps, username, password, dur=None, st_date=None, st_time=None):
		self.IP = IP
		self.inDir = inDir
		self.fps = fps
		self.username = username
		self.password = password
		self.duration = dur
		self.get_stime(st_date, st_time)
		self.capCam()

	def capCam(self):
		self.cap = cv2.VideoCapture(f'rtsp://{self.username}:
		{self.password}.@{self.IP}:554')
        self.cap.get(1)
		if self.start_time is not None:
			while self.start_time != datetime.datetime.now().replace 
			(microsecond=0):
				time.sleep(0.5)
			self.get_im()
		else:
			self.get_im()
		self.cap.release()

def get_im(self):
	if self.duration is not None:
		mFrame = self.getMaxFrames()
	else:
		mFrame = 0
	c = 0
	while (True):
		_, frame = self.cap.read()
		name = f'{self.inDir}/c{str(self.IP)[-2:]}_{str(t)[0:10]}_
		{str(t)[11:].replace(":", "-")}.jpg'
		cv2.imwrite(name, frame)
		c += 1
		time.sleep(0.5)
		if mFrame > 0 and c >= mFrame:
			break

def getMaxFrames(self):
	h = (self.time.find(':'))
	mFrame = (int(self.time[0:h]) * 3600 + int(self.time[h+1:h+3]) * 60 + int(self.time[h+4:])) * self.fps
	return mFrame

def get_stime(self, st_date, st_time):
	if st_date is None:
		st_date = str(datetime.datetime.now().date())
	if st_time is None:
		st_time = str(datetime.datetime.now().replace(microsecond=0).
		time())
	self.start_time = f'st_date st_time'

def det_chicken(model, source, th, imgsz, name, camset):
	detect_stall_1r.run(weights=model, source=source, conf=th, imgsz=imgsz, device=device, line_thickness=2, view_img=False, save_txt=True,    save_conf=False, nosave=False, project=inDir, name=name, exist_ok=True, color='yes', camset=camset, c_names=['chicken'], showMask=False)

procs = []
if __name__ == '__main__':
	procDet = multiprocessing.Process(target=det_chicken, args=(model, source, th, imgsz, name, camset))
	procDet.start()
	procs.append(procDet)
	time.sleep(1)
	for i in range(0, len(camset)):
		ip = '192.168.1.' + camset[i]
		proc = multiprocessing.Process(target=Camera, args=(ip, inDir, fps, t))
		proc.start()
		procs.append(proc)
	procIm = multiprocessing.Process(target=i2f, args=(inDir, camset))
	procIm.start()
	procs.append(procIm)
	for proc0 in procs:
		proc0.join()