Как я могу записать данные цвета пикселей в файл изображения bmp с помощью stb_image? - PullRequest
0 голосов
/ 19 апреля 2019

Я уже открыл файл bmp (одна шкала серого) и сохранил каждый цвет пикселя в новой строке в шестнадцатеричном виде.после некоторых процессов обработки данных (не в этом вопросе) мне нужно экспортировать изображение bmp из моих данных.Как я могу загрузить текстовый файл (данные) и использовать stb_image_write?

пиксель к изображению:

#include <cstdio>
#include <cstdlib>
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"


using namespace std;


int main() {
    FILE* datafile ;
    datafile = fopen("pixeldata.x" , "w");

    unsigned char* pixeldata ;//???

    char Image2[14] = "image_out.bmp";

    stbi_write_bmp(Image2, 512, 512, 1, pixeldata);

изображение к пикселю:

#include <cstdio>
#include <cstdlib>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"


using namespace std;


const size_t total_pixel = 512*512;
int main() {
    FILE* datafile ;
    datafile = fopen("pixeldata.x" , "w");

    char Image[10] = "image.bmp";
    int witdth;
    int height;
    int channels;
    unsigned char *pixeldata = stbi_load( (Image) , &witdth, &height, &channels, 1);

    if(pixeldata != NULL){
        for(int i=0; i<total_pixel; i++)
        {   
            fprintf(datafile,"%x%s", pixeldata[i],"\n");
        }
    }
}

1 Ответ

1 голос
/ 19 апреля 2019

В этом вопросе много недостатков - & ndash; слишком много, чтобы разобраться в комментариях ...

  1. Этот вопрос помечен как C ++. Почему подвержен ошибкам fprintf()? Почему не std::fstream? Он имеет аналогичные возможности (если не больше), но добавляет безопасность типов (которую не может обеспечить семейство printf()).

  2. Противоположность fprintf() - fscanf(). Форматеры похожи, но тип хранилища должен быть настроен в формататорах даже более тщательно, чем в fprintf().

  3. Если первым примером кода является попытка считывания пикселей с datafile.x ... Почему datafile = fopen("pixeldata.x" , "w");? Чтобы открыть файл с fopen() для чтения, он должен быть "r".

  4. char Image2[14] = "image_out.bmp"; правильно (если я правильно посчитал), но недружественное к обслуживанию. Пусть компилятор сделает всю работу за вас:

    char Image2[] = "image_out.bmp";
    

Обеспечить хранение данных пикселей с (в случае OP) фиксированным размером 512 раз; 512 байт, самое простое будет:

unsigned char pixeldata[512 * 512];

Хранение массива такого размера (512 & times; 512 = 262144 байт = 256 КБайт) в локальной переменной может рассматриваться как потенциальная проблема для некоторых людей. Альтернативой было бы использовать std::vector<unsigned char> pixeldata; вместо этого. (std::vector динамически распределяет хранилище в кучной памяти, где локальные переменные обычно в виде стековой памяти, которая, в свою очередь, обычно имеет ограниченный размер.)

Что касается std::vector<unsigned char> pixeldata;, я вижу два варианта:

  1. определение с предварительным распределением:

    std::vector<unsigned char> pixeldata(512 * 512);
    

    , чтобы его можно было использовать так же, как массив выше.

  2. определение без предварительного распределения:

    std::vector<unsigned char> pixeldata;
    

    Это позволило бы добавить каждый считанный пиксель до конца с std::vector::push_back().
    Возможно, стоит заранее зарезервировать окончательный размер, как известно с самого начала:

    std::vector<unsigned char> pixeldata;
    pixeldata.reserve(512 * 512); // size reserved but not yet used
    

Итак, вот как это может выглядеть в конечном итоге:

 #include <cstdio>
 #include <cstdlib>
 #include <iostream>
 #include <vector>
 #define STB_IMAGE_WRITE_IMPLEMENTATION
 #include "stb_image_write.h"

 int main()
 {
   const int w = 512, h = 512;
   // read data
   FILE *datafile = fopen("pixeldata.x" , "r");
   if (!datafile) { // success of file open should be tested ALWAYS
     std::cerr << "Cannot open 'pixeldata.x'!\n";
     return -1; // ERROR! (bail out) 
   }
   typedef unsigned char uchar; // for convenience
   std::vector<uchar> pixeldata(w * h);
   char Image2[] = "image_out.bmp";
   for (int i = 0, n = w * h; i < n; ++i) {
     if (fscanf(datafile, "%hhx", &pixeldata[i]) < 1) {
       std::cerr << "Failed to read value " << i << of 'pixeldata.x'!\n";
       return -1; // ERROR! (bail out) 
     }
   }
   fclose(datafile);
   // write BMP image
   stbi_write_bmp(Image2, w, h, 1, pixeldata.data());
   // Actually, success of this should be tested as well.
   // done
   return 0;
 }

Некоторые дополнительные примечания:

  1. Пожалуйста, возьмите этот код с зерном соли. Я не собирал и не проверял это. (Я оставляю это как задачу для OP, но буду реагировать на «сообщения об ошибках».)

  2. Я молча удалил using namespace std;: SO: Почему «использование пространства имен std» считается плохой практикой?

  3. Я добавил проверку успешности файловых операций. Файловые операции - это то, что всегда хорошо для отказа по многим причинам. Для записи в файл следует проверить даже fclose(). Записанные данные могут кэшироваться до тех пор, пока файл не будет закрыт, и простая запись кэшированных данных в файл может завершиться ошибкой (поскольку это может привести к переполнению доступного пространства тома).

  4. OP использовал магические числа (ширина и размер изображения), что считается плохой практикой. Это делает обслуживание кода недружественным и может быть труднее понять для других читателей: SO: Что такое магическое число и почему оно плохо?

...