본문 바로가기

프로젝트 이야기/드론을 이용한 식물 이상 탐지 시스템

드론을 이용한 식물 이상 탐지 시스템 - Connectwindow 구현

이번에는 Data Receive 버튼을 클릭했을 시 실행되는

Connectwindow 구현에 대하여 포스팅 할 것입니다.

 

Connectwindow는 위와 같이 구성되어 있습니다.

 

우선, 코드 전문입니다.

class ConnectWindow(Ui_Connect):
    def __init__(self, w2):
        self.IP = ""
        self.Port = ""
        self.workingFlag = 0
        self.successFlag = 0

        self.ConnectThread = DataRecvThread()
        self.ConnectThread.connectState.connect(self.connectLog)

        Ui_Connect.__init__(self)
        self.Dialog = w2
        self.setupUi(self.Dialog)

        self.connectButton.clicked.connect(self.RecvData)
        self.cancelButton.clicked.connect(self.CancelProcess)

    def RecvData(self):
        flist1 = os.listdir("temp/")
        flist2 = os.listdir("slice/")
        for f in flist1:
            os.remove("temp/" + f)
        for f in flist2:
            os.remove("slice/" + f)

        self.IP = self.IPplainTextEdit.toPlainText()
        self.Port = self.PortplainTextEdit.toPlainText()

        self.ConnectThread.IP = self.IP
        self.ConnectThread.Port = self.Port

        self.ConnectThread.start()

    def CancelProcess(self):
        if self.ConnectThread.workingFlag == 0:
            self.Dialog.close()
        elif self.ConnectThread.workingFlag == 1:
            self.connectLog(6)

    def connectLog(self, flag):
        if flag == 1:
            self.workingFlag = 1
            self.loglabel.setText("Receiving file...")
        elif flag == 2:
            self.loglabel.setText("Slicing video...")
        elif flag == 3:
            self.workingFlag = 0
            self.loglabel.setText("Data Receive Success!")
        elif flag == 4:
            self.loglabel.setText("Timeout! Please Reconnect.")
        elif flag == 5:
            self.loglabel.setText("Data Receive Fail...")
        elif flag == 6:
            self.loglabel.setText("Data Receiving now... Please Wait.")
        elif flag == 7:
            self.successFlag = 1 # receive success
        elif flag == 8:
            self.successFlag = 2 # receive fail

한 Widget은 하나의 클래스로 작성할 수 있습니다.

Mainwindow에서 했던 것과 같이 Ui_Connect를 상속받아 UI를 구성합니다.

 

    def __init__(self, w2):
        # IP와 Port를 저장할 함수, 각종 플래그를 선언
        self.IP = ""
        self.Port = ""
        self.workingFlag = 0
        self.successFlag = 0

        # 데이터 전송 기능을 구현한 쓰레드 변수 선언
        self.ConnectThread = DataRecvThread()
        self.ConnectThread.connectState.connect(self.connectLog)
        
        # UI 구성
        Ui_Connect.__init__(self)
        self.Dialog = w2
        self.setupUi(self.Dialog)

        # 각 버튼을 함수와 연결
        self.connectButton.clicked.connect(self.RecvData)
        self.cancelButton.clicked.connect(self.CancelProcess)

__init__ 함수입니다.

앞서 말했던 함수들과 비슷합니다.

필요한 정보들을 저장할 변수를 먼저 선언하고

처리 기능은 쓰레드로 구현했습니다.

마지막으로 clicked.connect() 함수를 이용해 버튼과 각 함수를 연결해 줍니다.

 

    def RecvData(self):
        flist1 = os.listdir("temp/")
        flist2 = os.listdir("slice/")
        # 경로 확인 후 각 캐시 폴더 초기화
        for f in flist1:
            os.remove("temp/" + f)
        for f in flist2:
            os.remove("slice/" + f)

        # 입력받은 텍스트를 변수에 저장
        self.IP = self.IPplainTextEdit.toPlainText()
        self.Port = self.PortplainTextEdit.toPlainText()

        # 쓰레드 변수에 IP, Port 전달
        self.ConnectThread.IP = self.IP
        self.ConnectThread.Port = self.Port

        #데이터 전송 쓰레드를 실행
        self.ConnectThread.start()

Connect 버튼을 클릭했을 때 실행되는 함수입니다.

먼저 전송받을 데이터에 문제가 없도록 각 캐시 폴더들을 초기화 합니다.

그 후 입력받은 IP와 Port를 변수에 저장하고 쓰레드로 전달합니다.

데이터를 전송받는 기능은 Analysis와 같이 쓰레드로 구현했습니다.

 

class DataRecvThread(QThread):
    connectState = pyqtSignal(int)
    IP = ""
    Port = ""
    workingFlag = 0
    def run(self):
        try:
            # 진행상황을 메시지로 전송하며 진행
            self.workingFlag = 1
            self.connectState.emit(1)
            server.so(self.IP, int(self.Port)) # 소켓통신 실행
            self.connectState.emit(2)
            vfs.capture() # 데이터 전처리 실행
            self.connectState.emit(3)
            self.connectState.emit(7)
            self.workingFlag = 0
        except:
            # 오류시 오류메시지 출력 후 종료
            self.connectState.emit(5)
            self.connectState.emit(8)
            self.workingFlag = 0

쓰레드가 실행되면 진행상황을 로그로 전달하며 데이터 전송이 진행됩니다.

 

server.so() 함수는 소켓통신을 구현한 함수입니다.

아래와 같이 구현되어 있습니다.

from socket import *
import socket
import os
import time
import sys
import subprocess

def fileName():
    # 이미지 파일 저장경로
    src = "../temp/"

    dte = time.localtime()
    Year = dte.tm_year
    Mon = dte.tm_mon
    Day = dte.tm_mday
    WDay = dte.tm_wday
    Hour = dte.tm_hour
    Min = dte.tm_min
    Sec = dte.tm_sec
    imgFileName = src + str(Year) + '-' + str(Mon) + '-' + str(Day) + '_' + str(Hour) + ':' + str(Min) + ':' + str(Sec) 
    return imgFileName

def so(IP, Port):
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind((IP, int(Port)))
    server_socket.listen(5)

    file_recive_cnt = 0

    print("TCPServer Waiting for client on port " + str(Port)) 
    # print(os.getcwd())

    for _ in range(2):

        # 클라이언트 요청 대기중 .
        client_socket, address = server_socket.accept()
        # 연결 요청 성공
        print("I got a connection from ", address)

        data = None

        # Data 수신
        while True:
            rev_path = 'temp/'
            rev_flist = os.listdir(rev_path)
            if ".DS_Store" in rev_flist:
                rev_flist.remove(".DS_Store")

            # if len(rev_flist) >= 2:
            #     for fname in rev_flist:
            #         os.system('rm ' + 'temp/' + fname)
            #         # subprocess.call('rm ' + 'temp/'+fname, shell=True)
            #     file_recive_cnt = 0

            img_data = client_socket.recv(1000000)
            data = img_data

            if img_data:
                while img_data:
                    print("recving Img...")
                    img_data = client_socket.recv(1000000)
                    data += img_data
                    print(len(data))
                else:
                    break

        # 받은 데이터 저장

        img_fileName = fileName()
        print(img_fileName)

        if file_recive_cnt == 0:
            img_fileName = img_fileName+".mp4"
        elif file_recive_cnt == 1:
            img_fileName = img_fileName+'.txt'

        img_file = open("temp/"+img_fileName, "wb")

        print("finish img recv")
        print(sys.getsizeof(data))

        img_file.write(data)
        img_file.close()
        client_socket.close()

        print("Finish ")

        data=None
        file_recive_cnt+=1

 

vfs.capture() 함수는 영상을 0.1초 단위로 slice하는 전처리를 수행하는 함수입니다.

참고로 vfs는 video frame slice의 약자입니다.

아래와 같이 구현되어 있습니다.

 

import cv2
import os

def capture():
    videopath = 'temp/'
    filelist = os.listdir(videopath)
    if ".DS_Store" in filelist:
            filelist.remove(".DS_Store")

    for fname in filelist:
        if ".mp4" in fname:
            videoname = fname
    video = cv2.VideoCapture(videopath + videoname)

    temp = int()
    index = int()
    result_frame_list = list()

    if not os.path.isdir("slice/"):
        os.mkdir("slice/") 

    while(video.isOpened()):
        ret, frame = video.read()
        print(ret)
        if index % 3 == 0:
            result_frame_list.append(frame)
            cv2.imwrite("slice/" + str(int(index/3)) + ".jpg",frame)
        
        if ret == True:
            temp = temp + 1
            index = index + 1
            
        if ret == False:
            break;
    video.release()
    print(temp)

 

해당 기능들은 이정민 학우(https://jeongmin-lee.tistory.com/)와 최남기 학우(https://namki-learning.tistory.com/)가 개발해 주었습니다.

 

 

전송 중 출력되는 각종 로그들은 플래그에 의해 관리되고

각 플래그마다 출력될 문자열들은 connectLog 함수에 정의되어 있습니다.

    def connectLog(self, flag):
        if flag == 1:
            self.workingFlag = 1
            self.loglabel.setText("Receiving file...")
        elif flag == 2:
            self.loglabel.setText("Slicing video...")
        elif flag == 3:
            self.workingFlag = 0
            self.loglabel.setText("Data Receive Success!")
        elif flag == 4:
            self.loglabel.setText("Timeout! Please Reconnect.")
        elif flag == 5:
            self.loglabel.setText("Data Receive Fail...")
        elif flag == 6:
            self.loglabel.setText("Data Receiving now... Please Wait.")
        elif flag == 7:
            self.successFlag = 1 # receive success
        elif flag == 8:
            self.successFlag = 2 # receive fail

connectLog 함수입니다.

전송받는 플래그에 따라 동작중인지 아닌지 판별하는 workingFlag를 관리하고

해당하는 작업상태의 메시지를 출력해 줍니다.

 

    def CancelProcess(self):
        if self.ConnectThread.workingFlag == 0:
            self.Dialog.close()
        elif self.ConnectThread.workingFlag == 1:
            self.connectLog(6)

마지막으로 Cancel 버튼을 클릭했을 때 실행되는 함수입니다.

데이터 전송중에 Widget을 종료할 경우 문제가 발생하므로 데이터 전송이 동작중인지 판별하여

데이터 전송 및 전처리중일 경우 안내 메시지를 출력하고

동작중이 아닐 경우에는 창을 동료할 수 있도록 만들었습니다.

 

 

Data Receive 기능은 이상입니다.