閑古鳥

オールドプログラマの日記。プログラミングとか病気(透析)の話とか。

OpenCV(C++)のcv::Matから一部を切り抜いて(ROI)フィルタをかける際に、元画像の画素値も参照される

PythonC++で、同じような実装をしたつもりが結果が異なるので少しハマりました。

# Python
import cv2
import numpy as np

mat = np.zeros((100, 100), np.uint8) + 100
cv2.rectangle(mat, (20, 20), (80, 80), 128, cv2.FILLED)
roi = mat[20:80, 20:80]
mat[20:80, 20:80] = cv2.blur(roi, (11, 11))
// C++
cv::Mat mat = cv::Mat1b::zeros(100, 100) + 100;
cv::rectangle(mat, cv::Rect(20, 20, 60, 60), cv::Scalar(128), cv::FILLED);
cv::Mat roi = mat(cv::Rect(20, 20, 60, 60));
cv::blur(roi, roi, { 11, 11 });
// この時点のmatが、Pythonと異なる

まあこの例だと一目なんですが、実際はもう少し入り組んでいたので最小化するのに苦労しました。roiにだけ処理したいのですが外側の画素値も見ちゃうんですね。それはそうか、という気もするような、しないような。本能的にコピーを避けようとしてしまうのですが、Pythonと同じ結果にしたければ、コピーすれば良いです。

cv::Mat mat = cv::Mat1b::zeros(100, 100) + 100;
cv::rectangle(mat, cv::Rect(20, 20, 60, 60), cv::Scalar{128}, cv::FILLED);
cv::Mat roi = mat(cv::Rect(20, 20, 60, 60)).clone();
cv::blur(roi, roi, { 11, 11 });
mat(cv::Rect(20, 20, 60, 60)) = roi;

逆にC++の元の実装と同じ挙動をPythonで実現したくなったら、結構面倒そうですね。

ちなみにblurのboderType引数にBORDER_ISOLATEDというものがありますが、これを使っても外側の値は参照されるようです。コメントにはdo not look outside of ROIと書いてあるんですが、これはまた暇なときにソースを読んでみようかな。