@Leroi_1

Не могу понять, почему у выходного файла идет смещение и ухудшение качества картинки?

Я сейчас учусь и пишу код на С++, разбираюсь с АРМ Неон, а у меня вылезло 2 неприятные ситуации. 1) Начало съезжать изображение, а почему не понимаю, вроде бы как все дела правильно, а код все равно берет и переносит часть изображения. 2) Работая без АРМ'а у меня вышел хороший четкий файл, а тут все никак не выходит сделать отличный результат. Либо изображение выходит размытым, либо очень резким.

Сам код:
#include <arm_neon.h>
#include <vector>
#include <algorithm>
#include <cstdint>
#include <iostream>
#include <fstream>
#include <chrono>

// Функция для чтения 8-битного BMP файла
std::vector<uint8_t> readBMP(const std::string &filename, int &width, int &height) {
    std::ifstream file(filename, std::ios::binary);
    if (!file) {
        std::cerr << "Ошибка: не удалось открыть файл " << filename << std::endl;
        return {};
    }

    // Чтение заголовка BMP
    uint8_t header[54];
    file.read(reinterpret_cast<char*>(header), 54);

    // Извлечение ширины и высоты изображения
    width = *reinterpret_cast<int*>(&header[18]);
    height = *reinterpret_cast<int*>(&header[22]);

    // Извлечение данных изображения
    int size = width * height;
    std::vector<uint8_t> data(size);
    file.read(reinterpret_cast<char*>(data.data()), size);

    return data;
}

// Функция для записи 8-битного BMP файла
void writeBMP(const std::string &filename, const std::vector<uint8_t> &data, int width, int height) {
    std::ofstream file(filename, std::ios::binary);
    if (!file) {
        std::cerr << "Ошибка: не удалось создать файл " << filename << std::endl;
        return;
    }

    // Создание заголовка BMP
    uint8_t header[54] = {
        0x42, 0x4D, 0, 0, 0, 0, 0, 0, 0, 0, 0x36, 0x04, 0, 0, 40, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    };

    int fileSize = 54 + 255 * 4 + width * height;
    header[2] = fileSize;
    header[3] = fileSize >> 8;
    header[4] = fileSize >> 16;
    header[5] = fileSize >> 24;

    header[18] = width;
    header[19] = width >> 8;
    header[20] = width >> 16;
    header[21] = width >> 24;

    header[22] = height;
    header[23] = height >> 8;
    header[24] = height >> 16;
    header[25] = height >> 24;

    file.write(reinterpret_cast<char*>(header), 54);

    // Запись цветовой палитры (256 цветов)
    for (int i = 0; i < 256; ++i) {
        uint8_t color[4] = { static_cast<uint8_t>(i), static_cast<uint8_t>(i), static_cast<uint8_t>(i), 0 };
        file.write(reinterpret_cast<char*>(color), 4);
    }

    // Запись данных изображения
    file.write(reinterpret_cast<const char*>(data.data()), data.size());
}

// Функция свёртки с использованием ARM NEON
std::vector<uint8_t> convolve_neon(const std::vector<uint8_t> &input, int width, int height) {
    std::vector<int32_t> intermediate_output(width * height, 0);
    std::vector<uint8_t> output(width * height, 0);
    const int8_t filter[25] = {
        1, 1, 1, 1, 1,
        1, 1, 1, 1, 1,
        0, 0, 0, 0, 0,
       -1,-1,-1,-1,-1,
       -1,-1,-1,-1,-1
    };

    // Загружаем фильтр в NEON регистры
    int8x16_t filter_low = vld1q_s8(filter);      // Загружаем первые 16 элементов
    int8x16_t filter_high = vld1q_s8(filter + 9); // Загружаем последние 16 элементов с перекрытием

    // Проходим по всем пикселям изображения
    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            int32x4_t sum = vdupq_n_s32(0);

            // Проходим по окну свёртки
            for (int fy = -2; fy <= 2; ++fy) {
                for (int fx = -2; fx <= 2; ++fx) {
                    int ix = x + fx;
                    int iy = y + fy;
                    // Проверяем границы изображения
                    if (ix >= 0 && ix < width && iy >= 0 && iy < height) {
                        // Загружаем пиксель
                        uint8x8_t pixel = vld1_u8(&input[iy * width + ix]);
                        // Преобразуем в int8x8_t для выполнения операций
                        int8x8_t pixel_signed = vreinterpret_s8_u8(pixel);

                        // Выбираем соответствующий регистр фильтра
                        int8x16_t filter_val = fx < 0 ? filter_low : filter_high;

                        // Умножаем пиксели на значения фильтра
                        int16x8_t result = vmull_s8(pixel_signed, vget_low_s8(filter_val));

                        // Преобразуем результат в сумму
                        int32x4_t temp = vpaddlq_s16(result);
                        sum = vaddq_s32(sum, temp);
                    }
                }
            }

            // Сохраняем промежуточный результат
            intermediate_output[y * width + x] = vgetq_lane_s32(sum, 0);
        }
    }

    // Нормализация значений
    int32_t max_val = *std::max_element(intermediate_output.begin(), intermediate_output.end());
    int32_t min_val = *std::min_element(intermediate_output.begin(), intermediate_output.end());
    for (size_t i = 0; i < output.size(); ++i) {
        output[i] = static_cast<uint8_t>(255.0 * (intermediate_output[i] - min_val) / (max_val - min_val));
    }

    return output;
}

int main() {
    int width, height;
    std::vector<uint8_t> image = readBMP("Scene384/0100.bmp", width, height);
    if (image.empty()) {
        return -1;
    }

    // Замер времени выполнения свёртки
    auto start = std::chrono::high_resolution_clock::now();
    std::vector<uint8_t> output = convolve_neon(image, width, height);
    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> duration = end - start;

    writeBMP("ARMOUT_0100.bmp", output, width, height);

    std::cout << "Обработка завершена. Результат сохранён в ARMOUT_0100.bmp" << std::endl;
    std::cout << "Время выполнения свёртки: " << duration.count() << " секунд" << std::endl;

    return 0;
}


6691129f4008c197410525.jpeg
  • Вопрос задан
  • 100 просмотров
Пригласить эксперта
Ответы на вопрос 1
wataru
@wataru Куратор тега C++
Разработчик на С++, экс-олимпиадник.
Потому что у вас неправильно применяется векторизация к задаче. У вас там свертка с ядром 5x5. Т.е. для одного выходного пикселя вы должны сделать 25 умножений и их сложить. Вы же делаете 25*8 уможений для каждого пикселя. Потом как-то странно их еще и суммируете.

Если у вас уж есть векторизация, то вы какие операции распаралеливаете? Вот эти 25 умножений же, правда? Значит у вас в программе не должно быть вообще вот этих циклов от -2 до 2. Вы эти операции сначала развернули в 25 отдельных, а потом их по 5 или 8 штук объединили в одну векторную операцию.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы