簡體   English   中英

位圖加載器C ++

[英]Bitmap loader C++

我知道我錯過了什么,但我不知道是什么。 這段代碼幫助我將圖像保存到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字節存儲器地址上對齊(SO: 結構填充和結構填充 )。 這意味着所有charshortint將占用4個字節,而前兩個字節在內存中只有幾個未使用的字節。 這通常是一件好事,因為當處理器可以從對齊的內存中讀取時,內存訪問 - 讀取和寫入 - 通常更快。 但是,如果您的結構由不同大小的成員組成,則在讀取或寫入文件時必須小心。 在讀取時,您的一些數據可能會消失在“未使用”的字節中,在寫入時,您會將這些未使用的字節保存到文件中。

你說你已經嘗試過報告的大小值而不是sizeof ,但這只能部分解決它。 正確的大小將寫入您的文件,但它仍然是錯誤的數據 - 因為您仍在編寫填充字節。

解決方案是告訴編譯器您希望在結構成員之間自動添加填充。 不同的編譯器有不同的方法,你沒有提到你的,但我上面提到的SO問題包含了一些例子。 如果所有其他方法都失敗了,請在編譯器的手冊中查找。

另一個策略是手動讀取和寫入每個結構成員而不是整個結構,但這在您的情況下幾乎沒有任何優勢。

這應該解決了右邊幾列被包裹在左邊的問題。 你的BMP閱讀器似乎很寬容,因為很多值最終都是“錯誤的”,但最終它仍然開始從錯誤的起始位置顯示圖像。


幸運的是,問題#2更容易。 從您的代碼:

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

(你忘了包含函數GetWidthStep但我猜它會返回單個位圖行的長度。)你的意思是首先抓住指向最后一行開頭的指針,然后抓住它上面的行,依此類推,直到第0行。 但是,你走了一條線!

如果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());

- 注意GetHeight()之后的-1 - 讓它工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM