C ++: проблемы производительности OpenCV при сканировании изображений

У меня есть около 20 изображений с цветовой кодировкой. Я хочу отсканировать каждое изображение и сопоставить пиксель с меткой, связанной с этим цветом. Я написал код ниже, однако для выполнения этой, казалось бы, простой задачи требуется около 30 минут. Изображения имеют разрешение 960 х 720.

Мой код:

void go_through_pixels(path &image_dir, string& ground_truth_suffix, string image_format, unordered_map<RGB, string> colors_for_labels){

if(!exists(image_dir)){
cerr << image_dir << " does not exist, prematurely returning" << endl;
exit(-1);
}

unordered_map<string, set<path> > label_to_files_map;

//initialise label_to_files_map
for(unordered_map<RGB, string>::iterator it = colors_for_labels.begin(); it != colors_for_labels.end(); it++){
label_to_files_map[it->second] = set<path>();
}

directory_iterator end_itr; //default construction provides an end reference

for(directory_iterator itr(image_dir); itr != end_itr; itr++){

path file = itr->path();
string filename = file.filename().string();
RGB rgb(0,0,0); //default rgb struct, values will be changed in the loop

if(extension(file) == image_format && filename.find(ground_truth_suffix) != string::npos){
//ground truth file
Mat img = imread(file.string(), CV_LOAD_IMAGE_COLOR);

for(int y = 0; y < img.rows; y++){
for(int x = 0; x < img.cols; x++){
//gives data as bgr instead of rgb
Point3_<uchar>* pixel = img.ptr<Point3_<uchar> >(y,x);
rgb.red = (int)pixel->z;
rgb.green = (int)pixel->y;
rgb.blue =(int)pixel->x;
string label = colors_for_labels[rgb];
label_to_files_map[label].insert(file);
cout << label << endl;
}
}
}
}
}

Позже я буду больше работать с этими данными, но упростил свой код до этого, просто чтобы попытаться найти проблему с производительностью.

Я обнаружил, что label_to_files_map[label].insert(file) вызывает большую часть задержки, так как при удалении требуется всего 3 минуты, чтобы просто отсканировать изображения. Я все еще думаю, что это слишком долго, но может быть неправильно?

Также в качестве набора insert занимает много времени (так как он должен проверять дубликаты перед вставкой) может кто-нибудь предложить лучшую структуру данных для использования здесь?

По сути, картинка может иметь, скажем, 100 пикселей, соответствующих зданию, 100 пикселей, соответствующих автомобилю и т. Д., Поэтому я просто хочу записать на карту label_to_files_map что этот файл (текущее сканируемое изображение) содержит здание (которое в этом случае обозначается конкретным значением rgb).

2

Решение

Проблема производительности заключается в том, что вы выполняете слишком много работы на пиксель.

Для каждого файла (непосредственно перед запуском ваших циклов for) создайте копию color_for_labels.

        Point3_<uchar> oldPixel;
for(int y = 0; y < img.rows; y++){
for(int x = 0; x < img.cols; x++){
//gives data as bgr instead of rgb
Point3_<uchar>* pixel = img.ptr<Point3_<uchar> >(y,x);
if(*pixel == oldPixel)
continue; // skip extra work
oldPixel = *pixel
rgb.red = (int)pixel->z;
rgb.green = (int)pixel->y;
rgb.blue =(int)pixel->x;
string label = copy_of_colors_for_labels[rgb];
if(label != null) {
label_to_files_map[label].insert(file);
copy_of_colors_for_labels[rgb] = null;
cout << label << endl;
}
}
}

Могут быть синтаксические ошибки (потому что я переписал это в браузере и не кодировал в C ++ в течение нескольких лет), но вышеприведенное должно убрать много дополнительной обработки.

3

Другие решения

Вы используете неправильные типы данных и неправильные функции. Вот предложение о том, как улучшить. Я полагаю, он будет запущен через несколько секунд.

Шаг 1 вашей работы — это таблица соответствия с 3-канального изображения на одноканальное изображение. Вы можете использовать cv :: LUT. Тем не менее, вам нужно сделать трюк, чтобы сделать это быстро.

Преобразуйте его в 4 байта на пиксель:

cv::Mat mat4bytes;
// add 8 bits to each pixel. the fill value is 255
cv::cvtColor(img, mat4bytes, CV_RGB2RGBA);
// this is a nice hack to interpret
// the RGBA pixels of the input image as integers
cv::Mat pseudoInteger(img.size(), CV_32UC1, mat4bytes.data);

Теперь вы можете применить LUT.

cv::Mat colorCoded;
// you have to convert your colors_for_labels lookup table
// like this:
lookupTable[i] =
((unsigned int)colors_for_labels.first.x << 24 ) +
((unsigned int)colors_for_labels.first.y << 16 ) +
((unsigned int)colors_for_labels.first.z << 8  ) +
255;
// make sure it is correct!!!
// and lookupTable data MUST be unsigned integer

cv::LUT(pseudoInteger, colorCoded, lookupTable);

РЕДАКТИРОВАТЬ На данный момент у вас есть в lookupTable значения, которые вы вычисляете в label

Последний шаг вашего расчета на самом деле гистограмма. Так почему бы вам не использовать функции гистограммы из OpenCV? проверить документы для calcHist()и посмотрите, как он лучше всего подходит для вашего алгоритма. Обратите внимание, что calcHist() можно выполнить гистограмму нескольких изображений одновременно, поэтому вы можете сохранить colorCoded изображения в векторе, затем извлеките гистограмму из всех них в одном.

1

В дополнение к другим ответам, касающимся оптимизации кода, рассмотрите возможность работы над гистограммой изображения. Несколько пикселей в вашем изображении будут иметь одинаковый цвет, поэтому сначала рассчитайте гистограмму, а затем выполните обработку для каждого отдельного цвета в вашем изображении. Это должно значительно ускорить процесс

0