Проблема:
Когда вы сделаете это:
cv::Mat frame1,frame2,for_each,cblue, cgreen, cred;
std::vector<cv::Mat> channels { cblue, cgreen, cred};
channels
будет иметь мелкую копию cv::Mat
cblue
, cgreen
и cred
,Это означает, что у них обоих будут одинаковые заголовки с указателем данных, который будет указывать на одно и то же место.
Затем вы делаете:
frame2.copyTo(cblue);
frame2.copyTo(cgreen);
frame2.copyTo(cred);
, который делает глубокую копию frame2 для каждого из cv :: Mat.Документация copyTo
гласит:
m - Матрица назначения.Если он не имеет правильного размера или типа перед операцией, он перераспределяется.
Это означает, что указатель на данные изменится, однако он не изменится на cv :: Mat внутривектор, они все равно будут указывать на nullptr
, но cblue
, cgreen
и cred
будут указывать на другое место.
Я проверил это с помощью этого кода:
cv::Mat frame(500, 500, CV_8UC3, cv::Scalar::all(111));
cv::Mat frame1, frame2, cblue, cgreen, cred;
std::vector<cv::Mat> channels{ cblue, cgreen, cred };
// at this point all data members of mat will point to nullptr except frame
cv::cvtColor(frame, frame, cv::COLOR_BGR2GRAY);
frame.convertTo(frame2, CV_8U);
frame2.copyTo(cblue);
frame2.copyTo(cgreen);
frame2.copyTo(cred);
// at this point all point to another place except the ones inside the vector
Возможные решения:
1) Создать ссылки, а не копии:
cv::Mat frame1, frame2;
std::vector<cv::Mat> channels(3);
cv::Mat& cblue = channels[0], &cgreen=channels[1], &cred=channels[2];
2) Использовать каналы напрямую, а не использовать другие переменные
frame2.copyTo(channels[0]);
frame2.copyTo(channels[1]);
frame2.copyTo(channels[2]);
3) Создайте вектор внутри цикла
frame2.copyTo(cblue);
frame2.copyTo(cgreen);
frame2.copyTo(cred);
std::vector<cv::Mat> channels { cblue, cgreen, cred};
cv::merge(channels, for_each);
4) Ваш код эквивалентен:
cvtColor(frame1, frame1, cv::COLOR_BGR2GRAY);
cvtColor(frame1, for_each, cv::COLOR_GRAY2BGR);
Это создаст 3-канальное изображениезначения серого, которые в основном являются копиями серого мата в каждом канале ...
5) Еще одна вещь:
frame1.convertTo(frame2,CV_8U);
Это не обязательно, потому что этоуже мат CV_8U
, потому что предыдущая инструкция преобразовала его в шкалу серого, которая равна CV_8U
, а затем вы даже можете создать вектор thБез глубокого копирования (глубокое копирование в for_each).
std::vector<cv::Mat> channels { frame1, frame1, frame1};
cv::merge(channels, for_each);
И еще одна вещь, не связанная с ошибкой:
cv::extractChannel (for_each, cblue, 0 );
cv::imshow("cropped_BGR",frame1);
cv::imshow("mod_BLUE",cblue);
будет отображать точното же самое изображение :) или должно по крайней мере.