Я сейчас учусь и пишу код на С++, разбираюсь с АРМ Неон, а у меня вылезло 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;
}