[Open CV] Expected Ptr<cv::UMat> for argument ‘src’
해안선 추출 작업 중에 제일 마지막 단계인 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인지 확인해보자.