1

私は何かが欠けていることを知っていますが、何がわかりません。このコードは、画像を BMP ファイルに保存するのに役立ちます。しかし、それを使用すると、画像の上部に黒いピクセルの行が表示され、画像が右にシフトします。何か案は ?ありがとう !

struct CVImporter::BITMAPFILEHEADER
{
    ushort bfType;
    uint   bfSize;
    uint   bfReserved;
    uint   bfOffBits;
};

struct CVImporter::BITMAPINFOHEADER
{
    uint  biSize;
    int   biWidth;
    int   biHeight;
    short biPlanes;
    short biBitCount;
    uint  biCompression;
    uint  biSizeImage;
    int   biXPelsPerMeter;
    int   biYPelsPerMeter;
    uint  biClrUsed;
    uint  biClrImportant;
};

struct CVImporter::RGBQUAD
{
    uchar rgbBlue;
    uchar rgbGreen;
    uchar rgbRed;
    uchar rgbReserved;
};

struct CVImporter::BITMAPINFO
{
    TBITMAPINFOHEADER bmiHeader;
    TRGBQUAD          bmiColors[256];
};

//-----------------------------------------------------------------------------
//
void
CVImporter::WriteBitmapFileHeader( std::ofstream& stream,
                                   const BITMAPFILEHEADER& header )
{
    WriteToStream( stream, header.bfType );
    WriteToStream( stream, header.bfSize );
    WriteToStream( stream, header.bfReserved );
    WriteToStream( stream, header.bfOffBits );
}

//-----------------------------------------------------------------------------
//
void
CVImporter::WriteBitmapInfoHeader( std::ofstream& stream,
                                   const BITMAPINFOHEADER& infoHeader )
{
    WriteToStream( stream, infoHeader.biSize );
    WriteToStream( stream, infoHeader.biWidth );
    WriteToStream( stream, infoHeader.biHeight );
    WriteToStream( stream, infoHeader.biPlanes );
    WriteToStream( stream, infoHeader.biBitCount );
    WriteToStream( stream, infoHeader.biCompression );
    WriteToStream( stream, infoHeader.biSizeImage );
    WriteToStream( stream, infoHeader.biXPelsPerMeter );
    WriteToStream( stream, infoHeader.biYPelsPerMeter );
    WriteToStream( stream, infoHeader.biClrUsed );
    WriteToStream( stream, infoHeader.biClrImportant );
}

//-----------------------------------------------------------------------------
//
void
CVImporter::WriteBitmapRGBQuad( std::ofstream& stream, const RGBQUAD& quad )
{
    WriteToStream( stream, quad.rgbBlue );
    WriteToStream( stream, quad.rgbGreen );
    WriteToStream( stream, quad.rgbRed );
    WriteToStream( stream, quad.rgbReserved );
}


//-----------------------------------------------------------------------------
//
void
CVImporter::WriteBitmapInfo( std::ofstream& stream,
                             const BITMAPINFO& info )
{
    WriteBitmapInfoHeader( stream, info.bmiHeader );
    for( uint i = 0; i < 256; ++i )
        WriteBitmapRGBQuad( stream, info.bmiColors[i] );
}

//-----------------------------------------------------------------------------
//
void
CVImporter::LoadBitmapFile( const CLString& fileName,
                            CVBitmap::Ptr bm ) throw(IOException)
{
    if( bm.IsNull() ) throw( IOException("Pointer should not be null", CL_ORIGIN) );

    // Verify the extension of the file
    CLString ext("");
    CLFileSystem::GetExtension( fileName, ext );
    if( ext != "bmp" )
        throw( IOException("Bad file extension (should be bmp)", CL_ORIGIN) );

    uint bytesPerPixel {3};
    uint imgDataSize = (bm->GetWidth()*bytesPerPixel)*bm->GetHeight();

    BITMAPFILEHEADER bmFile;
    BITMAPINFO bmInfo;

    bmFile.bfType = (ushort)0x4D42;
    bmFile.bfSize = imgDataSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFO);
    bmFile.bfReserved = 0;
    bmFile.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFO);

    bmInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmInfo.bmiHeader.biWidth = (ulong)bm->GetWidth();
    bmInfo.bmiHeader.biHeight = (ulong)bm->GetHeight();
    bmInfo.bmiHeader.biPlanes = 1;
    bmInfo.bmiHeader.biBitCount = (ushort) 8*bytesPerPixel;
    bmInfo.bmiHeader.biCompression = 0; //BI_RGB;
    bmInfo.bmiHeader.biSizeImage = imgDataSize;
    bmInfo.bmiHeader.biXPelsPerMeter = 0;
    bmInfo.bmiHeader.biYPelsPerMeter = 0;
    bmInfo.bmiHeader.biClrUsed = 256;
    bmInfo.bmiHeader.biClrImportant = 256;

    for( uint i = 0; i < 256; ++i )
    {
        bmInfo.bmiColors[i].rgbBlue = i;
        bmInfo.bmiColors[i].rgbGreen = i;
        bmInfo.bmiColors[i].rgbRed = i;
        bmInfo.bmiColors[i].rgbReserved = 0;
    }

    std::ofstream stream( fileName.c_str(), std::ios::binary );
    WriteBitmapFileHeader( stream, bmFile );
    WriteBitmapInfo( stream, bmInfo );

    uint padding = (4 - ((3 * bm->GetWidth()) % 4)) % 4;
    char padding_data[4] {0, 0, 0, 0};

    for( uint i = 0; i < bm->GetHeight(); ++i )
    {
        uchar* data_ptr = bm->GetBits() + ((bm->GetHeight()-i)*bm->GetWidthStep());
        stream.write( reinterpret_cast<char*>(data_ptr), sizeof(char) *
                      bm->GetByteSize() * bm->GetWidth() );

        stream.write( padding_data, padding );
    }

    stream.close();
}


//-----------------------------------------------------------------------------
//
template<typename T>
inline void
CVImporter::WriteToStream( std::ofstream& stream, const T& t )
{
    stream.write( reinterpret_cast<const char*>(&t), sizeof(T) );
}

ここに画像の説明を入力

4

1 に答える 1

4

コードには差し迫った問題が 2 つあります。

まず、構造体のメンバーは通常 4 バイトのメモリ アドレスに配置されます (SO:構造体のパディングと構造体のパッキング)。つまり、charshort、およびのすべてがint4 バイトを占有し、最初の 2 つはメモリ内に未使用のバイトがいくつかあるだけです。これは通常は良いことです。プロセッサがアライメントされたメモリから読み取ることができる場合、メモリ アクセス (読み取りと書き込み) は通常より高速になるからです。ただし、構造が異なるサイズのメンバーで構成されている場合は、ファイルの読み取りまたは書き込み時に注意する必要があります。読み取り時にデータの一部が「未使用」バイトに消え、書き込み時にこれらの未使用バイトがファイルに保存されます。

の代わりに報告されたサイズ値を既に試したと言いますsizeofが、それは部分的にしか解決しません。正しいサイズがファイルに書き込まれますが、パディング バイトをまだ書き込んでいるため、間違ったデータになります。

解決策は、構造体メンバー間に自動的にパディングを追加したくないことをコンパイラーに伝えることです。さまざまなコンパイラにはさまざまな方法があり、あなたの方法については言及していませんが、上記で指摘した SO の質問にはいくつかの例が含まれています。他のすべてが失敗した場合は、コンパイラのマニュアルで調べてください。

もう 1 つの戦略は、構造体全体ではなく、各構造体メンバーを手動で読み書きすることですが、それはあなたの状況ではほとんど利点がありません。

これにより、右側のいくつかの列が左側に折り返されるという問題が解決されるはずです。多くの値が「間違った」結果になるため、BMP リーダーは非常に寛容に見えますが、最終的には間違った開始位置から画像を表示し始めます。


幸いなことに、問題 2 の方が簡単です。あなたのコードから:

for( uint i = 0; i < bm->GetHeight(); ++i )
{
    uchar* data_ptr = bm->GetBits() + ((bm->GetHeight()-i)*bm->GetWidthStep());
    ...

(関数を含めるのを忘れていましたGetWidthStepが、1 つのビットマップ行の長さを返すと思います。) 最初に最後の行の先頭へのポインターを取得し、次にその上の行を取得するという意味です。 #0. ただし、1 行ずれています。

ではi=0、ポインタを として計算するstart + (height-i) * widthので、start + height * widthです。これは、画像の直後の「行」を指していますheight * width。「高さ」の実際の値を精神的に入力すると、それがわかります。

したがって、行1へのポインターをつかんでいますが、 に慣れてheightいるはずです。代わりにこれを使用してください:0height-1

uchar* data_ptr = bm->GetBits() + ((bm->GetHeight()-1-i)*bm->GetWidthStep());

-1--後で注意してくださいGetHeight()-- それを機能させるために。

于 2013-08-05T22:14:46.370 に答える