Проблемы с чтением растровых изображений с SD-карты
У меня проблемы с чтением файла растрового изображения с помощью библиотеки SD. Я написал небольшой заголовок, который читает несжатые 24-битные растровые файлы. Он отлично работает, когда я пробую его в Windows 10 MingW с fread() вместо SD.read(). На моем UNO он считывает только правильный адрес смещения, остальные части - мусор.
Для чистого красного растрового изображения размером 1x1 пиксель я получаю следующий ожидаемый результат в Windows с MingW:
Fname: ..\rot.bmp
Offset: 00000036
W * H: 1 * 1
Bit# 24
Cmp: 0
ArraySize: 1
---------------------------
RGB: ff0000
Выходы Arduino:
Fname: rot.bmp
Offset: 00000036
W * H: 0 * 2304
Bit# 58
Cmp: 0
ArraySize: 8
---------------------------
Error while reading BMP file
bmp.hpp
#ifndef BMP_HEADER
#define BMP_HEADER
#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_MEGA)
#include <SD.h>
#include<stdint.h>
#define BYTE uint8_t
#define WORD uint16_t
#define DWORD uint32_t
#define LONG int32_t
#else
#include<stdlib.h>
#include<windows.h>
#endif // определено(ARDUINO_AVR_UNO) || определено (ARDUINO_AVR_MEGA)
#pragma pack(2)
typedef struct
{
BYTE B;
BYTE G;
BYTE R;
} pixel;
typedef struct
{
WORD bfType;
DWORD bfSize;
DWORD bfReserved;
DWORD bfOffset; // смещение в байтовый массив
} bfheader;
typedef struct
{
DWORD biSize; // размер этого заголовка
LONG biWidth; //ширина изображения
LONG biHeight;//высота изображения
WORD biPlanes;
WORD biBitCount; //глубина цвета в битах на пиксель
DWORD biCompression; //0 для отсутствия (режим RGB)
DWORD biSizeImage; //размер изображения в байтах; может быть 0, если biCompression - RGB
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed; //наполнение палитры
DWORD biClrImportant; //больше палитры
} biheader;
//возвращает массив растровых изображений BGR
// предполагается, что заголовок и бизаголовок выделяются при выполнении
//возвращает ноль в случае неудачи
inline pixel* readBMP(const char* filename, bfheader* header, biheader* infoheader)
{
#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_MEGA)
SDLib::File f = SD.open(filename);
if(!f) return NULL;
f.read((uint8_t *)header, sizeof(bfheader)); // прочитать 14-байтовый заголовок файла
f.read((uint8_t *)infoheader, sizeof(biheader)); //читаем 40-байтовый информационный заголовок
#else
FILE* f = fopen(filename, "rb");
if(f == NULL) return NULL;
fread(header, sizeof(bfheader), 1, f);
fread(infoheader, sizeof(biheader), 1, f);
#endif // определено (ARDUINO_AVR_UNO) || определено (ARDUINO_AVR_MEGA)
if(infoheader->biCompression != 0 || infoheader->biBitCount != 24) //если файл не в кодировке RGB или не 24-битный
{
#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_MEGA)
f.close();
#else
fclose(f);
#endif // определено(ARDUINO_AVR_UNO) || определено (ARDUINO_AVR_MEGA)
return NULL;
}
int size = (int) infoheader->biSizeImage/sizeof(pixel); // получаем размер массива пикселей в байтах
pixel* data = (pixel*) malloc(sizeof(pixel)*size); // выделить
#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_MEGA)
f.seek(header->bfOffset); //установить файловый курсор на начальный адрес массива RGB
f.read((uint8_t *)data, sizeof(uint8_t)*size); // прочитать остальные данные сразу
f.close();
#else
fseek(f, header->bfOffset, SEEK_SET);
fread(data, sizeof(pixel), size, f);
fclose(f);
#endif //__рука__
return data;
}
#endif //BMP_HEADER
readbmp_test.ino
#include<arduino.h>
#include <SPI.h>
#include "bmp.hpp"
void setup() {
Serial.begin(9600);
while (!Serial) {
; // ждем подключения последовательного порта. Требуется только для родного порта USB
}
Serial.print("Initializing SD card...");
if (!SD.begin(4)) {
Serial.println("initialization failed!");
while (1);
}
Serial.println("initialization done.");
}
void loop()
{
char debug[160];
char rgbdata[8];
const char* const files[] = {"rot.bmp", "gruen.bmp", "blau.bmp"};
int files_len = 3;
bfheader header;
biheader infoheader;
pixel* image;
for(int i=0; i<files_len; i++)
{
image = readBMP(files[i], &header, &infoheader);
Serial.println("----------------------------");
sprintf(debug, "Fname: %s\n"
"Offset: %08lx\n"
"W * H: %ld * %ld\n"
"Bit# %u\n"
"Cmp: %lu\n"
"ArraySize: %lu\n"
"---------------------------\n",
files[i],
header.bfOffset,
infoheader.biWidth, infoheader.biHeight,
infoheader.biBitCount,
infoheader.biCompression,
infoheader.biSizeImage/sizeof(pixel));
Serial.print(debug);
if(image != NULL)
{
for(unsigned j=0; j<infoheader.biSizeImage/sizeof(pixel); j++)
{
sprintf(rgbdata, "%02x %02x %02x", image[j].R, image[j].G, image[j].B);
Serial.println(rgbdata);
}
}else{
sprintf(debug, "Error while reading BMP file\n");
Serial.print(debug);
}
free(image);
}
}
main.cpp //Тест MingW для Windows
#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_MEGA) // компилятор arduino хочет запустить этот файл main?
#else
#include<stdio.h>
#include "bmp.hpp"
int main(const int argc, const char** argv)
{
if(argc < 2) return -1;
bfheader header;
biheader infoheader;
for(int i=1; i<argc; i++)
{
pixel* image = readBMP(argv[i], &header, &infoheader);
if(image != NULL)
{
printf("Fname: %s\n"
"Offset: %08x\n"
"W * H: %d * %d\n"
"Bit# %d\n"
"Cmp: %d\n"
"ArraySize: %d\n"
"---------------------------\n",
argv[i],
header.bfOffset,
infoheader.biWidth, infoheader.biHeight,
infoheader.biBitCount,
infoheader.biCompression,
infoheader.biSizeImage/sizeof(pixel));
for(int j=0; j<infoheader.biSizeImage/sizeof(pixel); j++)
{
printf("RGB: %02x%02x%02x\n", image[j].R, image[j].G, image[j].B);
}
}else{
printf("Error while reading BMP file\n");
return 1;
}
free(image);
}
return 0;
}
#endif
Я действительно застрял на этом, я уже несколько раз переписывал его. Буду очень признателен за помощь.
@MatC, 👍0
Обсуждение1 ответ
f.read((uint8_t *)информационный заголовок, sizeof(информационный заголовок));
должно быть
f.read((uint8_t *)infoheader, sizeof(biheader));
размер переменной (указателя) всего 2. вы хотите прочитать 40 байт структуры biheader
и
f.read((uint8_t *)data, sizeof(uint8_t)*size)
должно быть
f.read((uint8_t *)данные, sizeof(пиксель)*размер)
Следующая проблема — %d и %x в sprintf. %d и %x считывают два байта на Arduino, а не 4 и не 1. Выведите значения без sprintf как Serial.println(number)
Ого, спасибо, что указали на это. Он читает ожидаемое количество байтов. Однако его вывод по-прежнему неверен. Fname: rot.bmp Смещение: 00000036 Ш * В: 0 * 1 Бит № 0 Команда: 1 Размер массива: 0, @MatC
Привет, @juraj, я ответил на твой комментарий к основному сообщению., @MatC
Привет @Juraj, я изменил строку формата, поэтому ожидается правильный тип данных, lu для uint32_t и т. Д. Для заголовка, который теперь работает нормально. Pixeldata по-прежнему неверны. Я последовал вашему совету и попытался отобразить Pixeldata, используя необработанный Serial.print
. Проблема все еще сохраняется. Fname: rot.bmp
Смещение: 00000036
Ш * В: 1 * 1
Бит № 24
Команда: 0
Размер массива: 1
---------------------------
58 0 0
мой текущий вывод, @MatC
@MatC, помогло?, @Juraj
- Сохранение файла .txt на SD-карту и чтение данных каждого содержимого в файл txt, а затем сохранение его в переменной
- Проблема с открытием нескольких текстовых файлов одновременно/по порядку с использованием <SD.h>
- DFPlayer для записи на SD-карту
- Попытка прочитать случайную строку с SD-карты
- SD-карта не инициализируется
- Что означают эти контакты? Куда мне их подключить?
- Режимы открытия файлов на SD-карте Arduino добавление/перезапись
- Понимание того, почему следует избегать «String» и альтернативных решений
распечатать количество байтов, прочитанных с помощью
file.read
(возвращаемое значение). попробуйте использоватьreadBytes
вместоread
, если количество прочитанных байтов не соответствует ожидаемому размеру, @Juraj@Juraj Я пробовал с readBytes и читал. Тот же результат: первое чтение возвращает ожидаемые 14 байтов. Второй возвращает только 2 вместо ожидаемых 40., @MatC
распечатайте структуру как байты с помощью println(b, HEX), чтобы увидеть необработанные байты, чтобы проверить их положение в полях структуры, @Juraj
Как мне это сделать?
Serial.println(*infoheader, HEX);
приводит к'HardwareSerial::println(biheader&, int)'
, @MatCЯ пробовал с
for(int i=0; i<sizeof(biheader); i++)
. Вывод, кажется, соответствует шестнадцатеричному дампу файла. Я поместил шестнадцатеричный дамп растрового изображения и вывод на [Pastebin](https://pastebin.com/YkhkXP8z), @MatCэто может быть проблема с printf. у вас есть %d, и он считывает только два байта на Arduino. распечатать значения без sprintf как Serial.println(число), @Juraj
Подсказка: начните с исправления чтения заголовка. Неверный размер. Используйте размер структуры, а не указатель. Используйте один и тот же sizeof для обоих вариантов., @Mikael Patel
@MikaelPatel спасибо за ваш вклад. Я исправил эту часть, но у меня все еще есть проблемы., @MatC
@Juraj Твой совет со sprintf был довольно хорош. Я включил все предупреждения во время компиляции и увидел, что типы данных не соответствуют строкам формата. Я исправил это, и теперь информация в моем заголовке отображается правильно. К сожалению, все еще остается проблема, поскольку чистое красное растровое изображение отображает значения ложных пикселей:
sprintf(rgbdata, "%02x %02x %02x", image[j].R, image[j].G, image[j]. Б); Serial.println(rgbdata);
отображает:c5 00 00
несмотря на то, что растровое изображение вообще не содержит C5., @MatC