У меня есть около 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).
Проблема производительности заключается в том, что вы выполняете слишком много работы на пиксель.
Для каждого файла (непосредственно перед запуском ваших циклов 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 ++ в течение нескольких лет), но вышеприведенное должно убрать много дополнительной обработки.
Вы используете неправильные типы данных и неправильные функции. Вот предложение о том, как улучшить. Я полагаю, он будет запущен через несколько секунд.
Шаг 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
изображения в векторе, затем извлеките гистограмму из всех них в одном.
В дополнение к другим ответам, касающимся оптимизации кода, рассмотрите возможность работы над гистограммой изображения. Несколько пикселей в вашем изображении будут иметь одинаковый цвет, поэтому сначала рассчитайте гистограмму, а затем выполните обработку для каждого отдельного цвета в вашем изображении. Это должно значительно ускорить процесс