Click here to Skip to main content
15,884,298 members
Articles / Desktop Programming / MFC

Convolution of Bitmaps

Rate me:
Please Sign up or sign in to vote.
4.07/5 (27 votes)
29 Mar 20063 min read 213.1K   2.4K   44  
Article discussing how to convolve images, specificially the convolution of bitmaps.
// ImageConvolutionView.cpp : implementation of the CImageConvolutionView class
//

#include "stdafx.h"
#include "ImageConvolution.h"

#include "ImageConvolutionDoc.h"
#include "ImageConvolutionView.h"

#include <math.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CImageConvolutionView

IMPLEMENT_DYNCREATE(CImageConvolutionView, CView)

BEGIN_MESSAGE_MAP(CImageConvolutionView, CView)
	//{{AFX_MSG_MAP(CImageConvolutionView)
		// NOTE - the ClassWizard will add and remove mapping macros here.
		//    DO NOT EDIT what you see in these blocks of generated code!
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CImageConvolutionView construction/destruction

CImageConvolutionView::CImageConvolutionView()
{
	// TODO: add construction code here
}

CImageConvolutionView::~CImageConvolutionView()
{
}

BOOL CImageConvolutionView::PreCreateWindow(CREATESTRUCT& cs)
{
	// TODO: Modify the Window class or styles here by modifying
	//  the CREATESTRUCT cs

	return CView::PreCreateWindow(cs);
}

/////////////////////////////////////////////////////////////////////////////
// CImageConvolutionView drawing

void CImageConvolutionView::OnDraw(CDC* pDC)
{
	CImageConvolutionDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	// TODO: add draw code for native data here

	if (pDoc->m_szFilename != _T(""))
	{
		if (GetDocument()->m_bKernelLoaded == FALSE)
		{//code copied from Zafir Anjum article on loading bitmaps
			HBITMAP hBitmap = (HBITMAP)::LoadImage(NULL,pDoc->m_szFilename,IMAGE_BITMAP,0,0,LR_LOADFROMFILE|LR_CREATEDIBSECTION);

			CBitmap bmp;
			bmp.Attach(hBitmap);

			CClientDC dc(this);
			CDC bmDC;
			bmDC.CreateCompatibleDC(&dc);
			CBitmap *pOldbmp = bmDC.SelectObject(&bmp);

			BITMAP  bi;
			bmp.GetBitmap(&bi);
			dc.BitBlt(0,0,bi.bmWidth,bi.bmHeight,&bmDC,0,0,SRCCOPY);
			bmDC.SelectObject(pOldbmp);
		}
		else
			ConvolveImage(m_kernel,m_nBias);
	
	}
	else
	{
		if (GetDocument()->m_bKernelLoaded == FALSE)
		{
			CBitmap bmp;
			bmp.LoadBitmap(IDB_BITMAP1);

			CClientDC dc(this);
			CDC bmDC;
			bmDC.CreateCompatibleDC(&dc);
			CBitmap *pOldbmp = bmDC.SelectObject(&bmp);

			BITMAP  bi;
			bmp.GetObject(sizeof(bi),&bi);
			dc.BitBlt(0,0,bi.bmWidth,bi.bmHeight,&bmDC,0,0,SRCCOPY);
			bmDC.SelectObject(pOldbmp);
		}
		else
			ConvolveImage(m_kernel,m_nBias);
	}
}

/////////////////////////////////////////////////////////////////////////////
// CImageConvolutionView diagnostics

#ifdef _DEBUG
void CImageConvolutionView::AssertValid() const
{
	CView::AssertValid();
}

void CImageConvolutionView::Dump(CDumpContext& dc) const
{
	CView::Dump(dc);
}

CImageConvolutionDoc* CImageConvolutionView::GetDocument() // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CImageConvolutionDoc)));
	return (CImageConvolutionDoc*)m_pDocument;
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CImageConvolutionView message handlers

void CImageConvolutionView::ConvolveImage(float kernel[][3], int nBias)
{
	CWaitCursor wait;
	GetDocument()->m_bKernelLoaded = TRUE;
	m_nBias = nBias;
	memcpy(m_kernel,kernel,sizeof(float)*9);
	//code partially copied from Zafir Anjum article on rotating bitmaps
	//Load bitmap from file
	HBITMAP hBitmap;
	if (GetDocument()->m_szFilename == _T(""))//If no file loaded use bitmap resource
		hBitmap = (HBITMAP)::LoadImage(GetModuleHandle(NULL),MAKEINTRESOURCE(IDB_BITMAP1),IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION);
	else//else use requested file
		hBitmap = (HBITMAP)::LoadImage(NULL,GetDocument()->m_szFilename,IMAGE_BITMAP,0,0,LR_LOADFROMFILE|LR_CREATEDIBSECTION);

	// Create a memory DC compatible with the display
	CDC sourceDC, destDC;
	sourceDC.CreateCompatibleDC( NULL );
	destDC.CreateCompatibleDC( NULL );

	// Get logical coordinates
	BITMAP bm;
	::GetObject( hBitmap, sizeof( bm ), &bm );


	// Create a bitmap to hold the result
	HBITMAP hbmResult = ::CreateCompatibleBitmap(CClientDC(NULL), bm.bmWidth, bm.bmHeight);

	HBITMAP hbmOldSource = (HBITMAP)::SelectObject( sourceDC.m_hDC, hBitmap );
	HBITMAP hbmOldDest = (HBITMAP)::SelectObject( destDC.m_hDC, hbmResult );

	//loop through each pixel
	for( int y = 0; y < bm.bmHeight; y++ )
	{
		for( int x = 0; x < bm.bmWidth; x++ )
		{
			//set pixel to color returned by convolve
			destDC.SetPixel(x,y,Convolve(&sourceDC,x,y,kernel,nBias,GetDocument()->m_bGrayscale));
		}
	}

	// Restore DCs
	::SelectObject( sourceDC.m_hDC, hbmOldSource );
	::SelectObject( destDC.m_hDC, hbmOldDest );

	//make CBitmap from convolution result
	CBitmap bmp;
	bmp.Attach(hbmResult);

	CClientDC dc(this);
	CDC bmDC;
	bmDC.CreateCompatibleDC(&dc);
	CBitmap *pOldbmp = bmDC.SelectObject(&bmp);

	//display result
	BITMAP  bi;
	bmp.GetBitmap(&bi);
	dc.BitBlt(0,0,bi.bmWidth,bi.bmHeight,&bmDC,0,0,SRCCOPY);
	bmDC.SelectObject(pOldbmp);
}

COLORREF CImageConvolutionView::Convolve(CDC* pDC, int sourcex, int sourcey, float kernel[3][3], int nBias,BOOL bGrayscale)
{
	float rSum = 0, gSum = 0, bSum = 0, kSum = 0;
	COLORREF clrReturn = RGB(0,0,0);
	for (int i=0; i <= 2; i++)//loop through rows
	{
		for (int j=0; j <= 2; j++)//loop through columns
		{
			//get pixel near source pixel
			/*
			if x,y is source pixel then we loop through and get pixels at coordinates:
			x-1,y-1
			x-1,y
			x-1,y+1
			x,y-1
			x,y
			x,y+1
			x+1,y-1
			x+1,y
			x+1,y+1
			*/
			COLORREF tmpPixel = pDC->GetPixel(sourcex+(i-(2>>1)),sourcey+(j-(2>>1)));
			//get kernel value
			float fKernel = kernel[i][j];
			//multiply each channel by kernel value, and add to sum
			//notice how each channel is treated separately
			rSum += (GetRValue(tmpPixel)*fKernel);
			gSum += (GetGValue(tmpPixel)*fKernel);
			bSum += (GetBValue(tmpPixel)*fKernel);
			//add the kernel value to the kernel sum
			kSum += fKernel;
		}
	}
	//if kernel sum is less than 0, reset to 1 to avoid divide by zero
	if (kSum <= 0)
		kSum = 1;
	//divide each channel by kernel sum
	rSum/=kSum;
	gSum/=kSum;
	bSum/=kSum;
	//add bias if desired
	rSum += nBias;
	gSum += nBias;
	bSum += nBias;
	//prevent channel overflow by clamping to 0..255
	if (rSum > 255)
		rSum = 255;
	else if (rSum < 0)
		rSum = 0;
	if (gSum > 255)
		gSum = 255;
	else if (gSum < 0)
		gSum = 0;
	if (bSum > 255)
		bSum = 255;
	else if (bSum < 0)
		bSum = 0;
	//return new pixel value
	if (bGrayscale)
	{
		int grayscale=0.299*rSum + 0.587*gSum + 0.114*bSum;
		rSum=grayscale;
		gSum=grayscale;
		bSum=grayscale;
	}

	clrReturn = RGB(rSum,gSum,bSum);
	return clrReturn;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
Programming using MFC and ATL for almost 12 years now. Currently studying Operating System implementation as well as Image processing. Previously worked on DSP and the use of FFT for audio application. Programmed using ADO, ODBC, ATL, COM, MFC for shell interfacing, databasing tasks, Internet items, and customization programs.

Comments and Discussions