Хорошо, первая подсказка в том, что изображение выглядит смещенным по кругу. Это намекает на то, что шаги неверны. Суть вашей проблемы проста:
n_rows = std::stoi(temp_line.substr(0, temp_line.find(' ')));
n_cols = std::stoi(temp_line.substr(temp_line.find(' ')+1,temp_line.size()));
, но в документации вы можете прочитать:
Каждый образ PPM состоит из следующего:
- «Волшебный c номер» для определения типа файла. Волшебное число c изображения ppm - это два символа "P6".
- Пробелы (пробелы, TAB, CRs, LFs).
- A ширина , отформатированный в десятичном виде в формате ASCII.
- Пробелы.
- A высота , снова в десятичном виде ASCII.
[...]
Ширина - столбцы, высота - строки , Так что это классическая ошибка, которую вы получаете при реализации обработки изображений: замена строк и столбцов.
С точки зрения дидактики c, почему вы делаете эту ошибку? Мое предположение: плохие инструменты отладки. Сделав рабочий пример из вашего вопроса (усилия, которые я бы сэкономил, если бы вы предоставили MCVE ), я побежал до конца загрузки изображения и использовал Image Watch, чтобы просмотреть содержимое вашего изображения с помощью @mem(raw_image_data, UINT8, 3, n_cols, n_rows, n_cols*3)
. Результат:
Хорошо, давайте попробуем поменять их местами: @mem(raw_image_data, UINT8, 3, n_rows, n_cols, n_rows*3)
. Результат:
Намного лучше. К сожалению, я не знаю, как указать RGB вместо BGR в псевдо-команде Image Watch @mem, поэтому неправильные цвета.
Тогда мы вернемся к вашему коду: скомпилируйте все предупреждения. Тогда я бы использовал больше функций std::stream
для анализа вашего ввода и меньше std::stoi()
или find()
. Избегайте выделения памяти с помощью std::vector
и создайте (возможно, шаблон) класс для изображений. Даже если вы придерживаетесь указателя на указатель, не делайте кратных new
для каждой строки: создайте один новый для указателя в строке 0, а другие указатели указывают на него:
uint8_t** image_grayscale = new uint8_t*[n_rows];
image_grayscale[0] = new uint8_t[n_rows*n_cols];
for (size_t i = 1; i < n_rows; ++i) {
image_grayscale[i] = image_grayscale[i - 1] + n_cols;
}
Тот же эффект, но легче освободить и управлять как один кусок памяти. Например, сохранение в виде PGM становится:
{
std::ofstream out("output.pgm", std::ios::binary);
out << "P5\n" << n_rows << " " << n_cols << "\n" << "255\n";
out.write(reinterpret_cast<char*>(image_grayscale[0]), n_rows*n_cols);
}
Заполните ваши границы! Используя один стиль выделения, который я показал вам, вы можете сделать это следующим образом:
uint32_t** gradient_magnitude = new uint32_t*[n_rows];
gradient_magnitude[0] = new uint32_t[n_rows*n_cols];
for (size_t i = 1; i < n_rows; ++i) {
gradient_magnitude[i] = gradient_magnitude[i - 1] + n_cols;
}
std::fill_n(gradient_magnitude[0], n_rows*n_cols, 0);
Наконец, величина градиента представляет собой целое значение от 0 до 360 (вы использовали uint32_t
). Тогда вы сохраняете только младший байт! Конечно, это неправильно . Вам необходимо отобразить от [0,360] до [0,255]. Как? Вы можете насытить (если больше 255 установить на 255) или применить линейное масштабирование (*255/360
). Конечно, вы можете делать и другие вещи, но это не важно.
Здесь вы можете увидеть результат в увеличенной версии трех случаев: насыщенный, масштабный, только LSB (неправильно): При неправильной версии вы видите темные пиксели, значение которых должно быть больше 255.