После слияния и извлечения канала BGR компиляция прошла успешно, но во время выполнения произошла ошибка: НЕЗАКОННАЯ ОПЕРАЦИЯ
Я пытаюсь создать 1 изображение CV_8UC3 из 3 разных изображений CV_8UC1, которые у меня уже есть, то есть я пытаюсь выделить разные одноканальные изображения, которые у меня уже есть, в одно 1 многомерное изображение.Скорее всего, приведенный ниже код работал безупречно напрямую с 3-канальным изображением, но при слиянии и извлечении он вызывает ошибку времени выполнения.НЕЗАКОННАЯ ОПЕРАЦИЯ
#include <opencv2/opencv.hpp>
#include <stdio.h>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include<vector>
typedef cv::Vec3b Pixel; // correct
struct Operator {
void operator ()(cv::Vec3b &pixel, const int * position) const
{
pixel[2]*=0.5;
}
};
int main(int argc, char** argv )
{
cv::VideoCapture cap(0);
if(!cap.isOpened())
return -1;
cv::Mat frame1,frame2,for_each,cblue, cgreen, cred;
std::vector<cv::Mat> channels { cblue, cgreen, cred};
for(;;)
{
cap >> frame1;
cvtColor(frame1, frame1, cv::COLOR_BGR2GRAY);
frame1.convertTo(frame2,CV_8U);
frame2.copyTo(cblue);
frame2.copyTo(cgreen);
frame2.copyTo(cred);
cv::merge(channels, for_each);
double t1 = (double)cv::getTickCount();
for_each.forEach<Pixel>(Operator());
t1 = ((double)cv::getTickCount() - t1)/cv::getTickFrequency();
std::cout<< "Parallel TEST time " << t1 << std::endl;
cv::extractChannel (for_each, cblue, 0 );
cv::imshow("cropped_BGR",frame1);
cv::imshow("mod_BLUE",cblue);
if (cv::waitKey(30) == 27)
{
std::cout << "esc key is pressed by user" <<std::endl;
break;
}
}
return 0;
}
Я не понимаю, откуда эта ошибка, любая помощь будет очень признательна, TIA.
2 ответа
Эта проблема:
Когда вы делаете это:
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
и затем вы даже можете создать вектор без глубокого копирования (это будет глубокое копирование в 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);
будет отображать точно такое же изображение:) или должен хотя бы.
Действительно, один из лучших ответов. Большое спасибо!!!
sol3: отлично сработало
double t1 = (double)cv::getTickCount();
std::vector<cv::Mat> channels { cblue, cgreen, cred};
cv::merge(channels, for_each);
for_each.forEach<Pixel>(Operator());
cv::extractChannel (for_each, cblue, 2 );
t1 = ((double)cv::getTickCount() - t1)/cv::getTickFrequency();
std::cout<< "Parallel TEST time " << t1 << std::endl;
sol1: однако это все еще дает мне
ошибка: в классе std:: vector нет члена с именем forEach channel.forEach(Operator()); ^~~~~~~ 16_vector_foreach_changedmaincode.cpp:54:31: ошибка: ожидаемое первичное выражение до маркера ">" каналов.forEach (Operator ());
я хотел
frame2.copyTo(cblue);
frame2.copyTo(cgreen);
frame2.copyTo(cred);
double t1 = (double)cv::getTickCount();
//std::vector<cv::Mat> channels { cblue, cgreen, cred};
std::vector<cv::Mat> channels(3);
cv::Mat& cblue = channels[0], &cgreen=channels[1], &cred=channels[2];
//cv::merge(channels, for_each);
channels.forEach<Pixel>(Operator());
cv::extractChannel (channels, cblue, 2 );
t1 = ((double)cv::getTickCount() - t1)/cv::getTickFrequency();
std::cout<< "Parallel TEST time " << t1 << std::endl;
я должен использовать оператор слияния здесь также?? @api55