为什么我必须将原始图像数据的宽度除以 3?

Why do I have to divide the width of my raw image data by 3?

所以我感觉快到终点线了。我正在做一个我工作需要的指纹 reader 项目。需要在 1 月中旬之前完成,所以我需要这个工作:)

我一直在互联网上四处寻找已经从我的 reader 中获取图像的图书馆,我被推荐给了 Griaule。长话短说,在经历了很多挫折之后,我得出的结论是 Griaule 已经过时了,对于它应该做的事情来说太复杂了,太昂贵了,而且总体上太难正常工作了。

所以我决定采用另一种方法,我使用 Microsoft 提供的示例,然后使用另一个库,我在获得图像后负责处理。

我现在可以扫描指纹,然后制作 BMP 文件。但是文件的宽高比很奇怪。尽管(如果没记错的话)扫描仪应该有更高的分辨率,但它看起来非常高而且挤在一起。

控制台显示图像应该是 256 x 360 但我必须将该宽度除以 3 才能使图像正常工作。所以结果显示为 85x360,看起来不太正确。

所以这是将图像保存为 BMP 的函数:

bool SaveBMP(BYTE* buffer, int width, int height, long paddedsize, LPCTSTR bmpfile) {
    BITMAPFILEHEADER bmfh;
    BITMAPINFOHEADER info;
    memset(&bmfh, 0, sizeof(BITMAPFILEHEADER));
    memset(&info, 0, sizeof(BITMAPINFOHEADER));
    bmfh.bfType = 0x4d42; // Don't question it. Magic Word (B and M). It's necessary. Seriously.
    bmfh.bfReserved1 = 0;
    bmfh.bfReserved2 = 0;
    bmfh.bfSize = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+paddedsize;
    bmfh.bfOffBits = 0x36;
    info.biSize = sizeof(BITMAPINFOHEADER);
    info.biWidth = width;
    info.biHeight = height;
    info.biPlanes = 1;

    info.biBitCount = 24;
    info.biCompression = BI_RGB;

    info.biSizeImage = 0;
    info.biXPelsPerMeter = 0x0ec4;
    info.biYPelsPerMeter = 0x0ec4;
    info.biClrUsed = 0;

    info.biClrImportant = 0;

    HANDLE file = CreateFile(bmpfile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

    unsigned long bwritten;
    if (WriteFile(file, &bmfh, sizeof(BITMAPFILEHEADER), &bwritten, NULL) == false) {
        CloseHandle(file);
        return false;
    }

    if (WriteFile(file, &info, sizeof(BITMAPINFOHEADER), &bwritten, NULL) == false) {
        CloseHandle(file);
        return false;
    }

    if (WriteFile(file, buffer, paddedsize, &bwritten, NULL) == false) {
        CloseHandle(file);
        return false;
    }

    CloseHandle(file);
    return true;
}

用于采集指纹的代码:

HRESULT CaptureSample()
{
    HRESULT hr = S_OK;
    WINBIO_SESSION_HANDLE sessionHandle = NULL;
    WINBIO_UNIT_ID unitId = 0;
    WINBIO_REJECT_DETAIL rejectDetail = 0;
    PWINBIO_BIR sample = NULL;
    SIZE_T sampleSize = 0;

    // Connect to the system pool. 
    hr = WinBioOpenSession(
        WINBIO_TYPE_FINGERPRINT,    // Service provider
        WINBIO_POOL_SYSTEM,         // Pool type
        WINBIO_FLAG_RAW,            // Access: Capture raw data
        NULL,                       // Array of biometric unit IDs
        0,                          // Count of biometric unit IDs
        WINBIO_DB_DEFAULT,          // Default database
        &sessionHandle              // [out] Session handle
        );
    if (FAILED(hr))
    {
        wprintf_s(L"\n WinBioOpenSession failed. hr = 0x%x\n", hr);
        goto e_Exit;
    }

    // Capture a biometric sample.
    wprintf_s(L"\n Calling WinBioCaptureSample - Swipe sensor...\n");
    hr = WinBioCaptureSample(
        sessionHandle,
        WINBIO_NO_PURPOSE_AVAILABLE,
        WINBIO_DATA_FLAG_RAW,
        &unitId,
        &sample,
        &sampleSize,
        &rejectDetail
        );
    if (FAILED(hr))
    {
        if (hr == WINBIO_E_BAD_CAPTURE)
        {
            wprintf_s(L"\n Bad capture; reason: %d\n", rejectDetail);
        }
        else
        {
            wprintf_s(L"\n WinBioCaptureSample failed. hr = 0x%x\n", hr);
        }
        goto e_Exit;
    }

    wprintf_s(L"\n Swipe processed - Unit ID: %d\n", unitId);
    wprintf_s(L"\n Captured %d bytes.\n", sampleSize);

    // Art "Messiah" Baker at Microsoft

    PWINBIO_BIR_HEADER BirHeader = (PWINBIO_BIR_HEADER)(((PBYTE)sample) + sample->HeaderBlock.Offset);
    PWINBIO_BDB_ANSI_381_HEADER AnsiBdbHeader = (PWINBIO_BDB_ANSI_381_HEADER)(((PBYTE)sample) + sample->StandardDataBlock.Offset);
    PWINBIO_BDB_ANSI_381_RECORD AnsiBdbRecord = (PWINBIO_BDB_ANSI_381_RECORD)(((PBYTE)AnsiBdbHeader) + sizeof(WINBIO_BDB_ANSI_381_HEADER));
    PBYTE firstPixel = (PBYTE)((PBYTE)AnsiBdbRecord) + sizeof(WINBIO_BDB_ANSI_381_RECORD);
    int width = AnsiBdbRecord->HorizontalLineLength;
    int height = AnsiBdbRecord->VerticalLineLength;

    wprintf_s(L"\n ID: %d\n", AnsiBdbHeader->ProductId.Owner);
    wprintf_s(L"\n Width: %d\n", AnsiBdbRecord->HorizontalLineLength);
    wprintf_s(L"\n Height: %d\n", AnsiBdbRecord->VerticalLineLength);
    wprintf_s(L"\n Horizontal Img. Res.: %d\n", AnsiBdbHeader->HorizontalImageResolution);
    wprintf_s(L"\n Horizontal Scan Img. Res.: %d\n", AnsiBdbHeader->HorizontalScanResolution);
    wprintf_s(L"\n Vertical Img. Res.: %d\n", AnsiBdbHeader->VerticalImageResolution);
    wprintf_s(L"\n Vertical Scan Img. Res.: %d\n", AnsiBdbHeader->VerticalScanResolution);
    wprintf_s(L"\n First Pixel: %d\n", firstPixel);
    wprintf_s(L"\n Element Count: %d\n", AnsiBdbHeader->ElementCount);

    bool b = SaveBMP(firstPixel, width, height, 0, L"C:\Users\smf\Desktop\fingerprint.bmp");
    wprintf_s(L"\n Success: %d\n", b);

e_Exit:
    if (sample != NULL)
    {
        WinBioFree(sample);
        sample = NULL;
    }

    if (sessionHandle != NULL)
    {
        WinBioCloseSession(sessionHandle);
        sessionHandle = NULL;
    }

    wprintf_s(L"\n Press any key to exit...");
    _getch();

    return hr;
}

我不太明白(因为我一般是 C++ 的新手,但精通高级语言)是为了让这个函数工作,我必须将传递给函数的任何宽度除以3. 如果我不这样做,图像将无法正常工作。

这背后的原因是什么,如果可能的话,我怎样才能让图片保持原来的宽度?

据我所知,WinBioCaptureSample 返回的图像是灰度位图,即每个像素使用 8 位。 您的 SaveBitmap 实现写入一个 24 位 RGB 位图。所以它需要的字节数是原始指纹位图的 3 倍(或者 - 正如您已经发现的那样 - 它会将图像缩小 3 倍)。

所以,为了解决这个问题,你需要在某个时候将每个字节增加三倍。在 SaveBitmap 中或在将数据传递给它之前。