TypeError: Layout of the output array image is incompatible 問題解決

本週在使用findContours的過程中遇到了以下問題:

TypeError: Layout of the output array image is incompatible with cv::Mat (step[ndims-1] != elemsize or step[1] != elemsize*nchannels)

感謝這篇文章:https://www.jianshu.com/p/cc3f4baf35bb,引導我往正確的方向進行思考。

先講結論造成以上問題的原因是:輸入與輸出的numpy格式不一致

我一開始的第一反應是爲什麼findContours函數會有“output array image”,我只關注contours。這其實是我在寫代碼中一個盲區,對於findContours函數我一般都是這麼寫:

_, contours, _ = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

 

對於第一個與第三個返回值現階段直接就跳過,這其實恰恰是產生問題的關鍵原因,完整的findContours函數輸出應該如下:

binary, contours, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

binary的輸出與image完全一樣,所以在使用的時候一般就忽略了這個參數,順便提一句在findContours函數的註釋中有這麼一句:

@note Since opencv 3.2 source image is not modified by this function.

opencv3.2之後就不會再修改image了,但是findContours函數爲什麼要改原圖?挺讓人費解的。到了opencv4.2之後這個返回值乾脆就被取消了,又造成了一系列的軟件兼容問題。

返回到這個錯誤上來,上面說了這麼多就是想說findContours函數其實是有一個image array輸出的。而這個array的要求非常嚴格。

我在處理image是按照channel維度對它進行遍歷,然後在每個channel上執行findContours函數,爲了確保後續操作不會影響原圖,我首先對每個channel執行了複製操作,這又是產生問題的另一個關鍵因素。當時是執行了copy.deepcopy()操作,這在我之前的使用過程中都是沒有問題的,包括我今天做實驗依然沒有問題,但是那天程序卻一直在報錯。而後改爲了numpy的copy函數才通過。

我首先把可以用於numpy的拷貝方法與其對應的flags輸出列在下面:

 

第一列表示原圖的flags屬性,one_channel表示使用遍歷方式獲得的一個channel的flags屬性,cv2.split表示使用split函數獲得的每一個channel的flags屬性,numpy.copy()表示首先使用遍歷方式獲得一個channel,而後調用copy函數得到的一個channel的flags屬性,最後copy.deepcopy()與numpy.copy()類似先遍歷獲得一個channel然後複製。

引用這篇博客中的一個關鍵結論:輸入與輸出必須完全一致,不單是值上的一致性,還需要保證array結構上的一致性。通過對比上表就可以發現,其實就是兩點不一樣:“C_CONTIGUOUS”與“OWNDATA ”,“OWNDATA ”非常好理解,使用切片方式獲取數據,自然不擁有數據,只擁有數據的引用。“C_CONTIGUOUS”表示數據是在一個單一的C風格的連續段中,由於numpy存儲數據是連續存儲最後一個維度,而後倒數第二個,以此類推。因此單獨獲得一個channel的數據也就不是連續的了。

現在也就是比較明確了,對於opencv輸入也是輸出的函數,必須保證array在flags層面上的一致性。

在使用過程中推薦使用split函數與numpy copy()函數,因爲我在使用copy.deepcopy過程中就遇到了“output array image is incompatible”問題。

PS:還要提一句findContours函數輸入可以是(n, n)維度的,不一定要(n,n, 1)維度。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章