7

我有一个由包含二维矩阵的矩阵库保存的文本文件,如下所示:

1 0 0 
6 0 4
0 1 1

每个数字都用一个彩色像素表示。我正在寻找一些关于如何解决这个问题的见解。如果需要更多信息,请随时询问。

编辑:我尝试过的另一种方法是:fwrite(&intmatrix, size,1, bmp_ptr);我传入矩阵指针的位置,它似乎没有输出可读的 BMP 文件。的值当然是,矩阵的类型是size来自犰狳线性代数库的矩阵。rows*colsarma::Mat<int>

编辑二:阅读表明我的大小可能应该是 rows*cols*4 如果我没记错的话,考虑到行的大小,关于这一点的任何指导也会很好。

4

5 回答 5

13

这是一个应用程序,它生成一个随机整数的文本文件,将它们读回,并将它们作为(大致正方形)每像素 32 位 .BMP 图像写入磁盘。

请注意,我对原始文本文件的格式、数字范围等做了一些假设,但它们都记录在代码中。通过这个工作示例,您应该能够在必要时轻松调整它们。

// IntToBMP.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <cstdint>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <random>
#include <ctime>
#include <memory>

#pragma pack( push, 1 ) 
struct BMP
{
    BMP();
    struct
    {
        uint16_t ID;
        uint32_t fileSizeInBytes;
        uint16_t reserved1;
        uint16_t reserved2;
        uint32_t pixelArrayOffsetInBytes;
    } FileHeader;

    enum class CompressionMethod : uint32_t {   BI_RGB              = 0x00, 
                                                BI_RLE8             = 0x01,
                                                BI_RLE4             = 0x02,
                                                BI_BITFIELDS        = 0x03,
                                                BI_JPEG             = 0x04,
                                                BI_PNG              = 0x05,
                                                BI_ALPHABITFIELDS   = 0x06 };

    struct
    {
        uint32_t headerSizeInBytes;
        uint32_t bitmapWidthInPixels;
        uint32_t bitmapHeightInPixels;
        uint16_t colorPlaneCount;
        uint16_t bitsPerPixel;
        CompressionMethod compressionMethod;
        uint32_t bitmapSizeInBytes;
        int32_t horizontalResolutionInPixelsPerMeter;
        int32_t verticalResolutionInPixelsPerMeter;
        uint32_t paletteColorCount;
        uint32_t importantColorCount;
    } DIBHeader;
};
#pragma pack( pop )

BMP::BMP()
{
    //Initialized fields
    FileHeader.ID                                   = 0x4d42; // == 'BM' (little-endian)
    FileHeader.reserved1                            = 0;
    FileHeader.reserved2                            = 0;
    FileHeader.pixelArrayOffsetInBytes              = sizeof( FileHeader ) + sizeof( DIBHeader );
    DIBHeader.headerSizeInBytes                     = 40;
    DIBHeader.colorPlaneCount                       = 1;
    DIBHeader.bitsPerPixel                          = 32;
    DIBHeader.compressionMethod                     = CompressionMethod::BI_RGB;
    DIBHeader.horizontalResolutionInPixelsPerMeter  = 2835; // == 72 ppi
    DIBHeader.verticalResolutionInPixelsPerMeter    = 2835; // == 72 ppi
    DIBHeader.paletteColorCount                     = 0;
    DIBHeader.importantColorCount                   = 0;
}

void Exit( void )
{
    std::cout << "Press a key to exit...";
    std::getchar();

    exit( 0 );
}

void MakeIntegerFile( const std::string& integerFilename )
{
    const uint32_t intCount = 1 << 20; //Generate 1M (2^20) integers
    std::unique_ptr< int32_t[] > buffer( new int32_t[ intCount ] ); 

    std::mt19937 rng;
    uint32_t rngSeed = static_cast< uint32_t >( time( NULL ) );
    rng.seed( rngSeed );

    std::uniform_int_distribution< int32_t > dist( INT32_MIN, INT32_MAX );

    for( size_t i = 0; i < intCount; ++i )
    {
        buffer[ i ] = dist( rng );
    }

    std::ofstream writeFile( integerFilename, std::ofstream::binary );

    if( !writeFile )
    {
        std::cout << "Error writing " << integerFilename << ".\n";
        Exit();
    }

    writeFile << buffer[ 0 ];
    for( size_t i = 1; i < intCount; ++i )
    {
        writeFile << " " << buffer[ i ];
    }
}

int _tmain(int argc, _TCHAR* argv[])  //Replace with int main( int argc, char* argv[] ) if you're not under Visual Studio
{
    //Assumption: 32-bit signed integers
    //Assumption: Distribution of values range from INT32_MIN through INT32_MAX, inclusive
    //Assumption: number of integers contained in file are unknown
    //Assumption: source file of integers is a series of space-delimitied strings representing integers
    //Assumption: source file's contents are valid
    //Assumption: non-rectangular numbers of integers yield non-rectangular bitmaps (final scanline may be short)
    //            This may cause some .bmp parsers to fail; others may pad with 0's.  For simplicity, this implementation
    //            attempts to render square bitmaps.

    const std::string integerFilename = "integers.txt";
    const std::string bitmapFilename = "bitmap.bmp";

    std::cout << "Creating file of random integers...\n";
    MakeIntegerFile( integerFilename );

    std::vector< int32_t >integers; //If quantity of integers being read is known, reserve or resize vector or use array

    //Read integers from file
    std::cout << "Reading integers from file...\n";
    {   //Nested scope will release ifstream resource when no longer needed
        std::ifstream readFile( integerFilename );

        if( !readFile )
        {
            std::cout << "Error reading " << integerFilename << ".\n";
            Exit();
        }

        std::string number;
        while( readFile.good() )
        {
            std::getline( readFile, number, ' ' );
            integers.push_back( std::stoi( number ) );
        }

        if( integers.size() == 0 )
        {
            std::cout << "No integers read from " << integerFilename << ".\n";
            Exit();
        }
    }

    //Construct .bmp
    std::cout << "Constructing .BMP...\n";
    BMP bmp;
    size_t intCount = integers.size();
    bmp.DIBHeader.bitmapSizeInBytes = intCount * sizeof( integers[ 0 ] );
    bmp.FileHeader.fileSizeInBytes = bmp.FileHeader.pixelArrayOffsetInBytes + bmp.DIBHeader.bitmapSizeInBytes;
    bmp.DIBHeader.bitmapWidthInPixels = static_cast< uint32_t >( ceil( sqrt( intCount ) ) );
    bmp.DIBHeader.bitmapHeightInPixels = static_cast< uint32_t >( ceil( intCount / static_cast< float >( bmp.DIBHeader.bitmapWidthInPixels ) ) );

    //Write integers to .bmp file
    std::cout << "Writing .BMP...\n";
    {
        std::ofstream writeFile( bitmapFilename, std::ofstream::binary );

        if( !writeFile )
        {
            std::cout << "Error writing " << bitmapFilename << ".\n";
            Exit();
        }

        writeFile.write( reinterpret_cast< char * >( &bmp ), sizeof( bmp ) );
        writeFile.write( reinterpret_cast< char * >( &integers[ 0 ] ), bmp.DIBHeader.bitmapSizeInBytes );
    }

    //Exit
    Exit();
} 

希望这可以帮助。

于 2012-09-03T05:58:17.850 回答
6

如果您选择正确的图像格式,这很容易。 PGM有一个 ASCII 变体,看起来几乎与您的矩阵一模一样,但有一个标题。

P2
3 3
6
1 0 0 
6 0 4
0 1 1

ASCII PGM 的魔力在哪里P2,大小是 3x3,6 是maxval. 我选择 6 是因为这是您提供的最大值,这使 6 变成白色(而 0 是黑色)。在 255 的典型 PGM 中,这与 8 位灰度图像一致。

PPM几乎同样简单,它每个像素只有 3 个颜色分量,而不是 1 个。

您可以使用任何需要 PPM(netpbm、ImageMagick、GIMP 等)的方式对这些图像进行操作。您可以将它们重新保存为与等效 BMP 大小基本相同的二进制 PPM。

于 2012-09-05T01:01:27.633 回答
5

要输出可读的 BMP 文件,您需要先放一个标头:

#include <WinGDI.h>

DWORD dwSizeInBytes = rows*cols*4; // when your matrix contains RGBX data)

// fill in the headers
BITMAPFILEHEADER bmfh;
bmfh.bfType = 0x4D42; // 'BM'
bmfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwSizeInBytes;
bmfh.bfReserved1 = 0;
bmfh.bfReserved2 = 0;
bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

BITMAPINFOHEADER bmih;
bmih.biSize = sizeof(BITMAPINFOHEADER);
bmih.biWidth = cols;
bmih.biHeight = rows;
bmih.biPlanes = 1;
bmih.biBitCount = 32;
bmih.biCompression = BI_RGB;
bmih.biSizeImage = 0;
bmih.biXPelsPerMeter = 0;
bmih.biYPelsPerMeter = 0;
bmih.biClrUsed = 0;
bmih.biClrImportant = 0;

现在在你写你的颜色信息之前,只写位图标题

fwrite(&bmfh, sizeof(bmfh),1, bmp_ptr);
fwrite(&bmih, sizeof(bmih),1, bmp_ptr);

最后是颜色信息:

fwrite(&intmatrix, size, sizeof(int), bmp_ptr);

请注意,块大小是 sizeof(int),因为您的矩阵不包含单个字符,而是每个值的整数。根据矩阵的内容,将值转换为 COLORREF 值可能是个好主意(检查 RGB 宏,也可以在 WinGDI.h 中找到)

于 2012-09-01T15:38:29.377 回答
1

我已经重写并评论了来自https://stackoverflow.com/a/2654860/586784的答案。我希望你觉得它足够清楚。

#include <cstddef>
#include <armadillo>
#include <map>
#include <cstdio>
#include <cassert>

///Just a tiny struct to bundle three values in range [0-255].
struct Color{
  Color(unsigned char red, unsigned char green, unsigned char blue)
    : red(red),green(green),blue(blue)
  {}

  ///Defualt constructed Color() is black.
  Color()
    : red(0),green(0),blue(0)
  {}

  ///Each color is represented by a combination of red, green, and blue.
  unsigned char red,green,blue;
};


int main(int argc,char**argv)
{

  ///The width of the image. Replace with your own.
  std::size_t w = 7;
  ///The height of the image. Replace with your own
  std::size_t h = 8;

  ///http://arma.sourceforge.net/docs.html#Mat
  ///The Armadillo Linear Algebra Library Mat constructor is of the following
  /// signature: mat(n_rows, n_cols).
  arma::Mat<int> intmatrix(h,w);

  ///Fill out matrix, replace this with your own.
  {

    ///Zero fill matrix
    for(std::size_t i=0; i<h; ++i)
      for(std::size_t j=0;j<w; ++j)
        intmatrix(i,j) = 0;

    intmatrix(0,3) = 1;
    intmatrix(1,3) = 1;


    intmatrix(2,2) = 6;
    intmatrix(2,4) = 6;

    intmatrix(3,2) = 4;
    intmatrix(3,4) = 4;


    intmatrix(4,1) = 6;
    intmatrix(4,2) = 6;
    intmatrix(4,3) = 6;
    intmatrix(4,4) = 6;
    intmatrix(4,5) = 6;

    intmatrix(5,1) = 1;
    intmatrix(5,2) = 1;
    intmatrix(5,3) = 1;
    intmatrix(5,4) = 1;
    intmatrix(5,5) = 1;


    intmatrix(6,0) = 4;
    intmatrix(6,6) = 4;

    intmatrix(7,0) = 6;
    intmatrix(7,6) = 6;

  }


  ///Integer to color associations. This is a map
  ///that records the meanings of the integers in the matrix.
  ///It associates a color with each integer.
  std::map<int,Color> int2color;

  ///Fill out the color associations. Replace this with your own associations.
  {
    ///When we see 0 in the matrix, we will use this color (red-ish).
    int2color[0] = Color(255,0,0);
    ///When we see 0 in the matrix, we will use this color (green-ish).
    int2color[1] = Color(0,255,0);
    ///When we see 0 in the matrix, we will use this color (blue-ish).
    int2color[4] = Color(0,0,255);
    ///When we see 0 in the matrix, we will use this color (grey-ish).
    int2color[6] = Color(60,60,60);
  }


  ///The file size will consist of w*h pixels, each pixel will have an RGB,
  /// where each color R,G,B is 1 byte, making the data part of the file to
  /// be of size 3*w*h. In addition there is a header to the file which will
  /// take of 54 bytes as we will see.
  std::size_t filesize = 54 + 3*w*h;


  ///We make an array of 14 bytes to represent one part of the header.
  ///It is filled out with some default values, and we will fill in the
  ///rest momentarily.
  unsigned char bmpfileheader[14] = {'B','M', 0,0,0,0, 0,0, 0,0, 54,0,0,0};

  ///The second part of the header is 40 bytes; again we fill it with some
  ///default values, and will fill in the rest soon.
  unsigned char bmpinfoheader[40] = {40,0,0,0, 0,0,0,0, 0,0,0,0, 1,0, 24,0};



  ///We will now store the filesize,w,h into the header.
  ///We can't just write them to the file directly, because different platforms
  ///encode their integers in different ways. This is called "endianness"
  ///or "byte order". So we chop our integers up into bytes, and put them into
  ///the header byte-by-byte in the way we need to.


  ///Encode the least significant 8 bits of filesize into this byte.
  ///Because sizeof(unsigned char) is one byte, and one byte is eight bits,
  ///when filesize is casted to (unsigned char) only the least significant
  ///8 bits are kept and stored into the byte.
  bmpfileheader[ 2] = (unsigned char)(filesize    );
  ///... Now we shift filesize to the right 1 byte, meaning and trunctate
  ///that to its least significant 8 bits. This gets stored in the next
  ///byte.
  bmpfileheader[ 3] = (unsigned char)(filesize>> 8);
  ///...
  bmpfileheader[ 4] = (unsigned char)(filesize>>16);
  ///Encodes the most significant 8 bits of filesize into this byte.
  bmpfileheader[ 5] = (unsigned char)(filesize>>24);

  ///Now we will store w (the width of the image) in the same way,
  /// but into the byte [5-8] in bmpinfoheader.
  bmpinfoheader[ 4] = (unsigned char)(       w    );
  bmpinfoheader[ 5] = (unsigned char)(       w>> 8);
  bmpinfoheader[ 6] = (unsigned char)(       w>>16);
  bmpinfoheader[ 7] = (unsigned char)(       w>>24);



  ///Now we will store h (the width of the image) in the same way,
  /// but into the byte [9-12] in bmpinfoheader.
  bmpinfoheader[ 8] = (unsigned char)(       h    );
  bmpinfoheader[ 9] = (unsigned char)(       h>> 8);
  bmpinfoheader[10] = (unsigned char)(       h>>16);
  bmpinfoheader[11] = (unsigned char)(       h>>24);

  ///Now we open the output file
  FILE* f = fopen("img.bmp","wb");

  ///First write the bmpfileheader to the file. It is 14 bytes.
  ///The 1 means we are writing 14 elements of size 1.
  ///Remember, bmpfileheader is an array which is basically
  ///the same thing as saying it is a pointer to the first element
  ///in an array of contiguous elements. We can thus say:
  ///write 14 bytes, starting from the spot where bmpfileheader points
  ///to.
  fwrite(bmpfileheader,1,14,f);
  ///Then write the bmpinfoheader, which is 40 bytes, in the same way.
  fwrite(bmpinfoheader,1,40,f);

  ///Now we write the data.
  ///For each row (there are h rows), starting from the last, going
  ///up to the first.
  ///We iterate through the rows in reverse order here,
  ///apparently in the BMP format, the image
  ///is stored upside down.
  for(std::size_t i=h-1; i != std::size_t(-1); --i)
  {
    ///For each column in the row,
    for(std::size_t j=0; j<w; ++j)
    {

      ///We retreive the integer of the matrix at (i,j),
      ///and assert that there is a color defined for it.
      assert (int2color.count(intmatrix(i,j)) != 0
        && "Integer in matrix not defined in int2color map");

      ///We somehow get the color for pixel (i,j).
      ///In our case, we get it from the intmatrix, and looking
      ///up the integer's color.
      Color color = int2color[intmatrix(i,j)];

      ///Now the colors are written in reverse order: BGR

      ///We write the color using fwrite, by taking a pointer
      ///of the (unsigned char), which is the same thing as
      ///an array of length 1. Then we write the byte.
      ///First for blue,
      fwrite(&color.blue,1,1,f);
      ///Same for green,
      fwrite(&color.green,1,1,f);
      ///Finally red.
      fwrite(&color.red,1,1,f);
    }


    ///Now we do some padding, from 0-3 bytes, depending in the width.
    unsigned char bmppad[3] = {0,0,0};
    fwrite(bmppad,1,(4-(w*3)%4)%4,f);
  }

  ///Free the file.
  fclose(f);


  return 0;
}
于 2012-09-07T19:03:15.660 回答
0

您是否有问题将矩阵视为图像或从代码中写入图像?

在前一种情况下,就像本杰克逊说的那样做

在后一种情况下,您想要传递 Arm::Matrix 的数据指针的地址,并且使用 fwrite 假设 Arm::Matrix 将其数据保存为一个连续的内存数组

[编辑]简要看看犰狳文档还告诉数据以列优先模式存储,但BMP假定行优先模式,所以你的图像看起来会翻转

[edit2] 使用犰狳矩阵函数,更简单

// assume A is a matrix
// and maxVal is the maximum int value in you matrix (you might scale it to maxVal = 255)
std::ofstream outfile("name.pgm");
oufile << "P2 " << sd::endl << a.n_rows << " " << a.n_cols << std::endl << maxVal << std::endl;
outfile << a << std::endl;
outfile.close();
于 2012-09-07T14:55:08.020 回答