使用 MFC 编写的 VC++2012 图像去底灰的示例

以下是一个使用 MFC 编写的 VC++2012 图像去底灰的示例,该示例加载一张位图、去掉背景并显示处理后的位图。

首先,在 MFC 应用程序中添加一个对话框,并在其上添加一个静态文本控件和一个按钮。将按钮命名为 “Open”,并为它添加响应函数 OnBnClickedOpen。

打开 Resource.h 文件,并添加以下几行代码:
#define IDC_STATIC_IMAGE 1000

这将定义一个名为 IDC_STATIC_IMAGE 的静态文本控件 ID。

然后,打开 DialogNameDlg.h 文件,并添加以下成员变量和函数声明:

private:
CBitmap m_bitmap;
BYTE* m_lpBitsSrc = nullptr;
BYTE* m_lpBitsDst = nullptr;
BITMAPINFOHEADER m_biSrc = {0};
BITMAPINFOHEADER m_biDst = {0};

void LoadImage(LPCTSTR lpszPath);
BOOL LoadBitmapData();
void RemoveBackground();
void ShowImage();

这些成员变量包括源图像和目标图像的像素数据指针、位图信息头等。同时,还声明了几个用于加载图像、去除背景、显示图像等操作的函数。

接下来,在 DialogNameDlg.cpp 文件中实现这些函数,具体代码如下:
void CDialogNameDlg::OnBnClickedOpen()
{
// 加载图像
LPCTSTR lpszFilter = _T(“JPEG Files (.jpg)|.jpg|Bitmap Files (.bmp)|.bmp|All Files (.)|.||”);
CFileDialog dlg(TRUE, NULL, NULL, OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, lpszFilter, this);
if (dlg.DoModal() == IDOK)
{
LoadImage(dlg.GetPathName());
RemoveBackground();
ShowImage();
}
}

void CDialogNameDlg::LoadImage(LPCTSTR lpszPath)
{
m_bitmap.Attach((HBITMAP)LoadImage(NULL, lpszPath, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE));
ASSERT(m_bitmap.m_hObject != NULL);

// 获取位图信息头和像素数据
CDC dc;
dc.CreateCompatibleDC(NULL);
dc.SelectObject(&m_bitmap);

m_biSrc.biSize = sizeof(BITMAPINFOHEADER);
m_biSrc.biBitCount = 32;
GetDIBits(dc.m_hDC, m_bitmap, 0, 0, NULL, (LPBITMAPINFO)&m_biSrc, DIB_RGB_COLORS);

m_lpBitsSrc = new BYTE[m_biSrc.biSizeImage];
GetDIBits(dc.m_hDC, m_bitmap, 0, m_biSrc.biHeight, m_lpBitsSrc, (LPBITMAPINFO)&m_biSrc, DIB_RGB_COLORS);

}

BOOL CDialogNameDlg::LoadBitmapData()
{
// 创建目标图像并分配内存
m_biDst.biSize = sizeof(BITMAPINFOHEADER);
m_biDst.biWidth = m_biSrc.biWidth;
m_biDst.biHeight = m_biSrc.biHeight;
m_biDst.biPlanes = 1;
m_biDst.biBitCount = 24;

int iNumBytes = ((m_biDst.biWidth * m_biDst.biBitCount + 31) & (~31)) / 8;
m_biDst.biSizeImage = iNumBytes * m_biDst.biHeight;

if (m_lpBitsDst != nullptr)
{
    delete[] m_lpBitsDst;
    m_lpBitsDst = nullptr;
}
m_lpBitsDst = new BYTE[m_biDst.biSizeImage];

if ((m_lpBitsSrc == NULL) || (m_lpBitsDst == NULL))
    return FALSE;

memcpy(&m_biDst, &m_biSrc, sizeof(BITMAPINFOHEADER));
m_biDst.biBitCount = 24;
m_biDst.biCompression = BI_RGB;

// 分配内存块
HGLOBAL hMemDst = GlobalAlloc(GMEM_MOVEABLE, m_biDst.biSizeImage);
BYTE* lpDst = (BYTE*)GlobalLock(hMemDst);

memcpy(lpDst, m_lpBitsDst, m_biDst.biSizeImage);

GlobalUnlock(hMemDst);

return TRUE;

}

void CDialogNameDlg::RemoveBackground()
{
if (!LoadBitmapData())
{
AfxMessageBox(_T(“Error: Failed to allocate memory!”));
return;
}

// 去掉底色灰度值最大的部分
int iWidthBytes = ((m_biDst.biWidth * m_biDst.biBitCount + 31) & (~31)) / 8;

for (int y = 0; y < m_biDst.biHeight; y++)
{
for (int x = 0; x < m_biDst.biWidth; x++)
{
BYTE* lpPixel = m_lpBitsDst + y * iWidthBytes + x * 3;

    BYTE b = *(lpPixel);
    BYTE g = *(lpPixel + 1);
    BYTE r = *(lpPixel + 2);

    double gray = 0.2126 * r + 0.7152 * g + 0.0722 * b;

    if (gray > 240)
    {
        *(lpPixel) = 255;
        *(lpPixel + 1) = 255;
        *(lpPixel + 2) = 255;
    }
}

}

}

void CDialogNameDlg::ShowImage()
{
// 显示图像
CRect rc;
GetDlgItem(IDC_STATIC_IMAGE)->GetClientRect(&rc);

CDC* pDC = GetDlgItem(IDC_STATIC_IMAGE)->GetDC();
CDC memDC;
CBitmap bmpMem;

memDC.CreateCompatibleDC(pDC);
bmpMem.CreateCompatibleBitmap(pDC, rc.Width(), rc.Height());
memDC.SelectObject(&bmpMem);

StretchDIBits(memDC.m_hDC,
0, 0, rc.Width(), rc.Height(),
0, 0, m_biDst.biWidth, m_biDst.biHeight,
m_lpBitsDst, (LPBITMAPINFO)&m_biDst, DIB_RGB_COLORS, SRCCOPY);

pDC->BitBlt(0, 0, rc.Width(), rc.Height(), &memDC, 0, 0, SRCCOPY);

ReleaseDC(pDC);

在 LoadImage 函数中,我们使用 LoadImage 和 GetDIBits 函数加载图像并读取其像素数据。在 LoadBitmapData 函数中,我们创建目标位图并为其分配内存。然后,在 RemoveBackground 函数中,我们遍历每个像素,并计算其灰度值。如果灰度值大于 240(即背景),则将该像素设为白色。最后,在 ShowImage 函数中,我们将修改后的像素数据绘制到静态文本控件上。

这是一个简单的示例,可以根据需要进行优化和扩展。
需要在 DialogNameDlg.h 文件中添加以下 include 和宏定义:
需要在 DialogNameDlg.h 文件中添加以下 include 和宏定义:

cpp
Copy code
#pragma once

#include

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif

#ifndef WINVER
#define WINVER 0x0501
#endif
这些 include 主要是为了使用一些 Windows API,并且在编译时使用正确的版本号。

接下来,在 DialogNameDlg.cpp 文件中实现 LoadImage 函数和 RemoveBackground 函数,具体代码如下:

cpp
Copy code
void CDialogNameDlg::LoadImage(LPCTSTR lpszPath)
{
HBITMAP hBitmap = (HBITMAP)LoadImage(nullptr, lpszPath, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION);

if (hBitmap != nullptr)
{
    BITMAP bm;
    GetObject(hBitmap, sizeof(bm), &bm);

    m_biSrc.biSize = sizeof(BITMAPINFOHEADER);
    m_biSrc.biWidth = bm.bmWidth;
    m_biSrc.biHeight = bm.bmHeight;
    m_biSrc.biPlanes = 1;
    m_biSrc.biBitCount = 24;

    int iNumBytes = ((m_biSrc.biWidth * m_biSrc.biBitCount + 31) & (~31)) / 8;
    m_biSrc.biSizeImage = iNumBytes * m_biSrc.biHeight;

    CDC* pDC = GetDC();
    CDC memDC;
    memDC.CreateCompatibleDC(pDC);

    void* lpvBits = nullptr;
    HBITMAP hDib = CreateDIBSection(memDC.m_hDC, (BITMAPINFO*)&m_biSrc, DIB_RGB_COLORS, &lpvBits, nullptr, 0);

    if (hDib != nullptr)
    {
        memDC.SelectObject(hDib);
        memDC.DrawState(CPoint(0, 0), CSize(m_biSrc.biWidth, m_biSrc.biHeight), hBitmap, DSS_NORMAL, (CBrush*)nullptr);
        GetDIBits(memDC.m_hDC, hDib, 0, m_biSrc.biHeight, lpvBits, (BITMAPINFO*)&m_biSrc, DIB_RGB_COLORS);
        m_lpBitsSrc = (BYTE*)lpvBits;

        DeleteObject(hDib);
    }

    ReleaseDC(pDC);
    DeleteObject(hBitmap);
}

}

void CDialogNameDlg::RemoveBackground()
{
if (m_lpBitsSrc == nullptr)
return;

// 创建目标位图
m_biDst.biSize = sizeof(BITMAPINFOHEADER);
m_biDst.biWidth = m_biSrc.biWidth;
m_biDst.biHeight = m_biSrc.biHeight;
m_biDst.biPlanes = 1;
m_biDst.biBitCount = 24;

int iNumBytes = ((m_biDst.biWidth * m_biDst.biBitCount + 31) & (~31)) / 8;
m_biDst.biSizeImage = iNumBytes * m_biDst.biHeight;

std::vector<BYTE> data(m_biDst.biSizeImage, 0);
m_lpBitsDst = data.data();

if (m_lpBitsDst == nullptr)
    return;

// 去掉底色灰度值最大的部分
for (int y = 0; y < m_biDst.biHeight; y++)
{
    for (int x = 0; x < m_biDst.biWidth; x++)
    {
        BYTE* lpPixelSrc = m_lpBitsSrc + y * (m_biDst.biWidth * 3) + x * 3;
        BYTE* lpPixelDst = m_lpBitsDst + y * iNumBytes + x * 3;

        BYTE b = *(lpPixelSrc);
        BYTE g = *(lpPixelSrc + 1);
        BYTE r = *(lpPixelSrc + 2);

        double gray = 0.2126 * r + 0.7152 * g + 0.0722 * b;

        if (gray > 240)
        {
            *(lpPixelDst) = 255;
            *(lpPixelDst + 1) = 255;
            *(lpPixelDst + 2) = 255;
        }
        else
        {
            *(lpPixelDst) = b;
            *(lpPixelDst + 1) = g;
            *(lpPixelDst + 2) = r;
        }
    }
}

}
在 LoadImage 函数中,我们使用 LoadImage 函数加载图像,并使用 CreateDIBSection 函数创建一个与位图兼容的 DIB。然后,我们使用 DrawState 函数将位图绘制到 DIB 上,并使用 GetDIBits 函数读取像素数据。

在 RemoveBackground 函数中,与之前一样,我们遍历每个像素,并计算其灰度值。如果灰度值大于 240(即背景),则将该像素设为白色;否则,将该像素保留,并将它复制到目标位图中。

最后,在 ShowImage 函数中,我们需要稍作修改,以便使用 CreateCompatibleBitmap 函数创建一个与静态文本控件兼容的位图,并将修改后的像素数据绘制到其中:

cpp
Copy code
void CDialogNameDlg::ShowImage()
{
// 显示图像
CRect rc;
GetDlgItem(IDC_STATIC_IMAGE)->GetClientRect(&rc);

CDC* pDC = GetDlgItem(IDC_STATIC_IMAGE)->GetDC();
CDC memDC;
CBitmap bmpMem;

memDC.CreateCompatibleDC(pDC);
bmpMem.CreateCompatibleBitmap(pDC, m_biDst.biWidth, m_biDst.biHeight);
memDC.SelectObject(&bmpMem);

SetStretchBltMode(memDC.m_hDC, COLORONCOLOR);

StretchDIBits(memDC.m_hDC,
    0, 0, m_biDst.biWidth, m_biDst.biHeight,
    0, 0, m_biDst.biWidth, m_biDst.biHeight,
    m_lpBitsDst, (LPBITMAPINFO)&m_biDst, DIB_RGB_COLORS, SRCCOPY);

pDC->BitBlt(0, 0, rc.Width(), rc.Height(), &memDC, 0, 0, SRCCOPY);

ReleaseDC(pDC);

}
现在,我们已经成功地实现了图像去底灰的功能。但是,请注意,这只是一种简单的方法,效果可能不如其他更高级的算法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值