[OpenCV 디버깅] 카메라 QR 인식 시, 문제 해결

2023. 1. 20. 20:30프로젝트 로그/테스트x솔루션 JIG 개발기

반응형

문제

testX 솔루션의 JIG S/W는 OpenCV 기반으로 카메라를 제어 하여, 테스트 하려는 모듈의 QR 코드 값을 읽어 시리얼 코드를 가져 오는 기능이 있습니다.

 

해당 기능을 구현하고 테스트 하는 중, 화면에서 엉뚱한 시리얼 코드( 정확하게 말하자면 이전에 올려 놨던 모듈의 시리얼 코드)를 읽어 오는 문제가 있었습니다.

 

예를 들어 

첫번째 테스트를 위해 2, 5, 7번 위치에 아래 시리얼을 가지고 있는 모듈을 올려 놓고 테스트를 진행 합니다.

  • 2: XX:XX:XX:5C:7B:05
  • 5: XX:XX:XX:5C:7C:E3
  • 7: XX:XX:XX:5D:57:03

두번째 테스트 때는 모듈의 위치를 아래와 같이 바꿔서 테스트를 진행 합니다.

  • 1: XX:XX:XX:5C:73:E3
  • 3: XX:XX:XX:5D:57:03
  • 4: XX:XX:XX5C:7B:05
  • 5: XX:XX:XX:6F:2B:36

 

이 때, JIG S/W에서 CH-7번에 XX:XX:XX:5D:57:03에 있다고 판단되는 문제가 발생 했습니다. 해당 시리얼을 가지고 있는 모듈은 첫번째 테스트 때 CH-7번에 놓고 테스트 한 모듈 입니다.

 

아무래도 테스트를 진행 할 때 OpenCV에서 Camera의 영상 데이터를 read하고, 테스트가 멈췄을 때는 Camera read 함수를 호출하지 않고 있는데, 다음 번 테스트 때 이전 영상 데이터를 불러와서 발생한 현상으로 예상 됩니다.

 

원본 코드

  • pause 상태가 아니라면 FRAME POS를 0으로 초기화 하고 카메라에서 영상 데이터를 read 함
    • cv2.CAP_PROP_POS_FRAMES를 0으로 초기화 하면 이전에 읽은 데이터를 초기화 할 것으로 예상 하였지만, 실제로는 동작 하지 않음

 

    def run(self):
        while self.alive:
            time.sleep(0.1)

            if self.isPause == True:
                continue
	
            currentTimeStamp = round(time.time() * 1000)
            if self.isCalibrationMode == False:
                if( (currentTimeStamp - self.startTimeStampResume) > self.CONFIG.THREAD_WORING_TIMEOUT_MS ):
                    CameraManagerLogger.instance().logger().debug("Timeout : {0}".format((currentTimeStamp - self.startTimeStampResume)))
                    self.setPause()


            self.capture.set(cv2.CAP_PROP_POS_FRAMES, 0)
            ret, self.frame = self.capture.read()
            if ret:
                height, width, channels = self.frame.shape

                #print("Height : {0}, Width : {1}, Channels : {2}".format(height, width, channels))

                ''' image Pre Processing '''
                # self.imagePreProcessing()
                ''' set SubThread's imageReady True '''
                self.setSubThreadsImageReady()
                ''' draw QRCodes Rectangles '''
                self.BarcodeObj.drawQRRect(self.frame)
                ''' draw Region Dividing Lines'''
                self.Region.drawRegion(self.frame)
                ''' draw Frame Image '''
                self.drawImage(width, height, channels)
            
        self.capture.release()
        
        CameraManagerLogger.instance().logger().debug(f"VideoThread was terminated")

 

첫번째 수정 코드

  • 아래와 같이 pause 상태일 때나, pause 상태가 아닐 때나 카메라 영상 데이터를 계속 read 함.
  • 이렇게 수정하니 위 문제가 발생하지는 않았지만 CPU 점유율을 약 100 ~ 200%를 상시 사용하는 문제가 있음.

 

    def run(self):
        while self.alive:
            time.sleep(0.1)

            # 주의 : 
            ret, self.frame = self.capture.read()
            if ret == False:
                continue

            if self.isPause == True:
                # PAUSE 상태에서도 카메라 read는 계속 해야 함.
                # 카메라 이미지 Flush를 위해
                continue

            currentTimeStamp = round(time.time() * 1000)
            if self.isCalibrationMode == False:
                if( (currentTimeStamp - self.startTimeStampResume) > self.CONFIG.THREAD_WORING_TIMEOUT_MS ):
                    CameraManagerLogger.instance().logger().debug("Timeout : {0}".format((currentTimeStamp - self.startTimeStampResume)))
                    self.setPause()

            height, width, channels = self.frame.shape

            #print("Height : {0}, Width : {1}, Channels : {2}".format(height, width, channels))

            ''' image Pre Processing '''
            # self.imagePreProcessing()
            ''' set SubThread's imageReady True '''
            self.setSubThreadsImageReady()
            ''' draw QRCodes Rectangles '''
            self.BarcodeObj.drawQRRect(self.frame)
            ''' draw Region Dividing Lines'''
            self.Region.drawRegion(self.frame)
            ''' draw Frame Image '''
            self.drawImage(width, height, channels)
            
        self.capture.release()
        
        CameraManagerLogger.instance().logger().debug(f"VideoThread was terminated")

 

두번째 수정 코드

  • Pause 상태에서 capture.grab() 함수를 수행
    • videocapture::grab() 함수는 카메라 장치에 다음 프레임을 획득하라는 명령을 내림
    • 기타 참고
      • videocapture::retrieve() 함수는 획득한 프레임을 실제로 받아오는 함수
      • videocaputre::read() 함수는 위 grab() + retrieve() 함수를 합쳐 놓은 함수
  • 아래와 같이 pause 상태에서 grab() 함수를 호출하니, 이전 프레임을 flush 할 수 있으면서 CPU 점유율을 높이지 않고 위 문제를 해결함을 확인 함.

 

    def run(self):
        while self.alive:
            time.sleep(0.1)

            if self.isPause == True:
                # 카메라 이미지 Flush를 위해 
                self.capture.grab()
                continue
	
            currentTimeStamp = round(time.time() * 1000)
            if self.isCalibrationMode == False:
                if( (currentTimeStamp - self.startTimeStampResume) > self.CONFIG.THREAD_WORING_TIMEOUT_MS ):
                    CameraManagerLogger.instance().logger().debug("Timeout : {0}".format((currentTimeStamp - self.startTimeStampResume)))
                    self.setPause()


            # 주의 : 
            ret, self.frame = self.capture.read()
            if ret:
                height, width, channels = self.frame.shape

                #print("Height : {0}, Width : {1}, Channels : {2}".format(height, width, channels))

                ''' image Pre Processing '''
                # self.imagePreProcessing()
                ''' set SubThread's imageReady True '''
                self.setSubThreadsImageReady()
                ''' draw QRCodes Rectangles '''
                self.BarcodeObj.drawQRRect(self.frame)
                ''' draw Region Dividing Lines'''
                self.Region.drawRegion(self.frame)
                ''' draw Frame Image '''
                self.drawImage(width, height, channels)
            
        self.capture.release()
        
        CameraManagerLogger.instance().logger().debug(f"VideoThread was terminated")
반응형