[Open CV] Expected Ptr<cv::UMat> for argument ‘src’

1 minute read

해안선 추출 작업 중에 제일 마지막 단계인 Edge Detection에서 자꾸 이런 에러가 뜬다.

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<timed exec> in <module>

<ipython-input-16-d58dd581f2d3> in Pipeline(tiff_path)
     43 #     laplacian[laplacian==0] = np.nan
     44 
---> 45     sobel = cv2.Sobel(img, cv2.CV_8U, 1, 1, 3).astype('float')
     46 #     sobel[sobel==0] = np.nan
     47 

TypeError: Expected Ptr<cv::UMat> for argument 'src'

위에 있는 건 Sobel Edge Detection이지만 Canny나 Laplacian도 똑같은 에러가 뜬다.
웃기는 건 Scikit-Image로 적용한 Prewitt이나 Roberts는 문제없이 잘 된다는 것.

구글링해봤을 때는 뭐 파일 경로가 잘못 되었다던가 했는데 skimage는 경로를 잘 인식하고 OpenCV는 못 한다는 법이 없지 않은가.

어쨌든, 해결법은 OpenCV documentation에서 찾았는데, image라는 입력 변수를 single-channel 8-bit image로만 받는다는 것이다.
Edge Detection을 하기 전에 이것 저것 하느라고 출력이 0~1 사이의 값을 가진 float32형태로 나왔는데, 이걸 OpenCV는 인식을 못 하고 Scikit-Image는 인식한다는 것이었다.

따라서 이 문제를 해결하기 위해서는 단순히 데이터 형식을 uint8로 바꿔주면 된다.

img = np.uint8(img*255)

본인의 경우에는 img 변수가 0~1의 값을 가진 float32형태이므로 255를 곱한 후 uint8로 바꾸었다.

  • 참고로, Scikit-Image의 경우 Float형식의 데이터를 지원하는 만큼 Edge Detection 시 np.nan값도 그냥 무시하면서 무난무난하게 넘어가는데, OpenCV의 경우 될 리가 없다. Scikit-Image가 갑자기 OpenCV보다 좋아보인다.
  • 잠시 초보를 위한 설명을 하자면, int8과 uint8은 8개의 bit를 사용해서 정수를 표현하는 방식이다. bit는 0과 1 중 하나를 표현할 수 있는 컴퓨터 데이터 형식의 가장 작은 단위이며, 이게 8개가 있으므로 2**8=256개의 정수를 표현할 수 있다. 그리고 uint8은 0~255까지의 정수를 표현하며, int8은 bit 하나를 떼어 음수/양수를 표현하여 -128~127까지의 정수를 표현한다. 일반적인 JPEG나 PNG같은 이미지는 픽셀 하나의 채널 값 하나를 0~255로, 즉 uint8로 표현한다.

결론

OpenCV에서 TypeError: Expected Ptr<cv::UMat> for argument 'src' 에러가 뜬다면 이미지의 경로가 잘못 되지는 않았는지, 아니면 이미지의 형식이 uint인지 확인해보자.