CV - PlantCV를 이용한 엽면적 측정기 개발
PlantCV를 이용한 Leaf Segmentation 및 엽면적 계산



CV - 지난 글




프로젝트 배경

엽면적은 재배와 식물 연구에서 중요한 의미를 많이 가지고 있는 요소라 볼 수 있다. 생육조사는 현 식물의 상태를 판단하는 중요한 과정이다. 그렇다면 이렇게 중요한 엽면적이 식믈의 재배 및 연구에서 어떤 의미를 가지는 걸까?

광합성에 영향


증산에 영향


이렇게 엽면적은 식물의 재배와 연구에서 중요한 부분을 차지한다. 그렇다면 어떻게 엽면적을 구할 수 있을까?


기기를 통한 엽면적 측정 (Li3100c)

관행적으로 엽면적을 측정하는 방식으로는 기기를 활용하는 방식이 있다. 현재 연구실에서 사용하고 있는 Li3100c가 이에 해당한다. Li3100c는 롤러에 잎이 말려들어가고, 아래에서 비친 빛에 잎이 가리는 부분의 면적을 산출하여 잎의 면적을 계산한다.


Li3100c


ImageJ

ImageJ는 이미지의 조정, 필터링, 측정 및 분석 기능을 제공하는 소프트웨어로 실험을 통해 얻은 이미지 자료를 분석하는데에 용이하게 사용된다. 엽면적의 경우 잎을 찍은 사진에 스케일링을 하고, 잎의 면적을 폴리건으로 잡아주어 잎의 면적을 산출할 수 있다.


imageJ


위 방법들은 관행적으로 잎의 면적을 산출하는 방법이다. 하지만 이러한 방법들에는 각각의 단점이 존재한다.

이러한 단점들을 커버하면서 잎의 면적을 계산할 수 있는 방법이 없을까 평소에 고민하고 있었고, 최근에 ‘식물표현체학’에서 배운 이미지 프로세싱 기법과 평소에 관심있던 대형 딥러닝 기술을 활용하여 잎의 면적을 계산할 수 있는 프로그램을 이번 스마트농업프로그래밍 수업을 통해 만들어 보자고 생각하여 이번 프로젝트를 진행하게 되었다.


기존 Computer Vision 기술과 딥러닝

이미지를 컴퓨터를 이용하여 처리하는 Computer Vision (CV) 기술은 이미 오랜 과거 부터 연구되어 오던 분야이다. 예를들어

최근 딥러닝을 이용한 이미지 처리 기술의 발달로 이미지 처리 기술의 발달은 가속화하게 된다.


image


기존의 컴퓨터 비전 방식은 특정한 작업에 최적화된 알고리즘을 사용하는 반면, 딥러닝 기반 방식은 데이터로부터 자동으로 특징을 학습하여 더 넓은 범위의 문제에 적용할 수 있다. 딥러닝의 등장으로 컴퓨터 비전 분야는 혁신적인 발전을 이루었으며, 이는 이미지 인식, 객체 감지, 분류 작업 등에서 획기적인 성능 향상을 가져왔다.



이번 프로젝트에서는 이러한 두가지의 Computer Vision 기술을 바탕으로 엽면적 측정 알고리즘을 만들고 두 기술의 결과를 비교해보면 재밌을 것 같다는 생각을 하게되었고, 이를 실행에 옮겨보았다.


제작 장비

제작에 사용된 장비는 다음과 같다.

젯슨 나노 보드는 소형 보드로 라즈베리파이에 GPU를 탑재한 싱글보드 컴퓨터라 이해하면 된다. 젯슨 나노를 사용하여 딥러닝이나 머신러닝과 같은 소프트웨어를 구현할 수 있는 장점이 있다. 이러한 이유로 최근 AI가 적용된 프로젝트에 메인보드로 많이 쓰이고 있는 추세이다.

아두이는 오픈소스를 기반으로 한 단일보드인 마이크로컨트롤로 완성된 보드와 관련 개발환경을 말한다. 2005년 이탈리아의 IDII에서 하드웨어에 익숙치 않은 학생들이 자신들의 디자인 작품을 손쉽게 제어할 수 있도록 하기 위해 고안되었다. 이 중 아두이노 우노는 R3 보드를 사용하며, 총 44개의 핀과 단자들로 구성되어있다. 각 핀과 단자들은 아두이와 다른 보드 또는 센서들의 제어에 이용될 수 있으며, 다양한 응용이 가능하다.



회로도 구성



엽면적 산출 방법

엽면적 산출을 위한 방법은 간단한 수학적 개념으로 접근하였다. 고정된 높이(25cm)와 비율(1280 x 1080)로 이미지를 촬영하여 비율을 통일하고, 해당 이미지에서 잎이 차지하는 픽셀수와 실제 잎의 면적간의 비율을 알아내면 다른 잎의 이미지를 가지고도 실제 잎의 면적을 추정할 수 있을 것이라 생각하였다. 해당 수식은 아래와 같다.


PlantCV

PlantCV (Plant Computer Vision)는 식물 연구와 관련된 이미지 분석을 위한 오픈 소스 파이썬 라이브러리이다. 생물학자와 식물학자들이 식물의 성장, 건강, 생리학적 반응 등을 분석할 수 있도록 설계되었다.


RGB to LAB

다음은 RGB를 LAB 색공간으로 해석하는 코드이다. Red, Green, Blue 세개의 색공간으로 되어있는 이미지를 LAB 색공간 (L: 밝기, a: Red, Green, b:Yellow, Blue)으로 전환하는 코드이다.

import cv2
import os
from plantcv import plantcv as pcv

now = datetime.now
ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
time.sleep(2)

capture_counter = 0

def process_image(filename):
    if not os.path.exists('processed_images'):
        os.makedirs('processed_images')

    img, path, _ = pcv.readimage(filename=os.path.join('capture_images', filename))

    b = pcv.rgb2gray_lab(rgb_img=img, channel='b')
    b_thresh = pcv.threshold.binary(gray_img=b, threshold=138, object_type='light')

    mask_pixel_count = cv2.countNonZero(b_thresh)
    processed_filename = os.path.join('processed_images', f'{filename[:-4]}_processed.jpg')
    cv2.imwrite(processed_filename, b_thresh)  

    actual_leaf_area = mask_pixel_count * 0.000508
    actual_leaf_area = round(actual_leaf_area, 2)
    print("Actual leaf area: ", actual_leaf_area, 'cm^2')

    ser.write(str(actual_leaf_area).encode())

    return actual_leaf_area


CSI Camera Module

다음은 OpenCV를 이용하여 젯슨 나노와 연결된 라즈베리파이 카메라 (CSI Camera Module)을 제어하는 방법이다.

젯슨 나노는 라즈베리파이와 다르게 picamera모듈을 사용할 수 없기 때문에 OpenCV를 통해 카메라 제어 파이프라인을 만들어주어야 한다. 다음은 해당 파이프라인을 이용해서 RGB 이미지를 캡쳐하고 저장하는 코드이다.

def gstreamer_pipeline():
    return (
        "nvarguscamerasrc ! "
        "video/x-raw(memory:NVMM), width=(int)1280, height=(int)1080, format=(string)NV12, framerate=(fraction)30/1 ! "
        "nvvidconv ! video/x-raw, format=(string)BGRx ! "
        "videoconvert ! video/x-raw, format=(string)BGR ! appsink"
    )

def capture_image(filename):
    if not os.path.exists('capture_images'):
        os.makedirs('capture_images')

    full_filename = os.path.join('capture_images', filename)
    cap = cv2.VideoCapture(gstreamer_pipeline(), cv2.CAP_GSTREAMER)
    if cap.isOpened():
        ret, frame = cap.read()
        if ret:
            cv2.imwrite(full_filename, frame) 
        cap.release()
    else:
        print("Unable to open the camera")


Arduino 제어

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup() {
  lcd.init();
  lcd.backlight();
  Serial.begin(9600);
  pinMode(5, INPUT_PULLUP); // 5번 핀 ==> 진행 버튼
  pinMode(4, INPUT_PULLUP); // 4번 핀 ==> 종료 버튼
}

void loop() {
  int buttonState = digitalRead(5);
  int exitButtonState = digitalRead(4); 

  if (buttonState == LOW) {
    Serial.println("YES");
    while (digitalRead(5) == LOW); 
  }

  if (exitButtonState == LOW) {
    Serial.println("NO"); 
    while (digitalRead(4) == LOW); 
  }
  
  if (Serial.available() > 0) {
    String leafArea = Serial.readString();
    lcd.setCursor(0, 0);
    lcd.print("Leaf Area:");
    lcd.setCursor(0, 1);
    lcd.print(leafArea + " cm^2");
  }

  delay(1500);
}


파이썬 제어

def main():
    results = []
    image_count = 0
    while True:
        if ser.in_waiting > 0:
            line = ser.readline().decode('utf-8').rstrip()
            if line == "YES":
                image_count += 1
                filename = f"csi_camera_image_{image_count}.jpg"
                capture_image(filename)
                results.append({'Filename': filename, 'Leaf_Area': process_image(filename)})
            elif line == "NO":
                break  

    df = pd.DataFrame(results)
    df.to_csv(f'{datetime.now().strftime("%Y%m%d_%H")}_results.csv', index=False)
    print("Results saved to 'results.csv'")
    print("Exiting program.")


전체적인 flow chart는 다음과 같다.


결과


PlantCV를 통한 엽면적 마스킹 결과는 다음과 같다.


계산한 엽면적을 출력하는 LCD 화면은 다음과 같다.



저장된 csv 결과는 다음과 같다.


성능