c++MFC图像处理CImage类常⽤操作代码原⽂作者:aircraft 我看了⼀下发现关于c++下的CImage图像处理类的图像处理相关的介绍真的是⽐较少,因为我要做⼤⼆的数据结构的课程设计,要⽤纯c++语⾔去实现(⽼师不让调⽤opencv之类图像处理库),所以⾃⼰就尝试写了很多操作处理的代码,下⾯都是我课设⽤到的代码段,我已经降低耦合度分离出来了,并且全部都是⽤指针对内存的操作,这样速度⽐较快,现在就全部分享出来吧。
前⼈栽树,后⼈乘凉望与诸君共勉。
MFC图像处理CImage类常⽤操作CImage类头⽂件为#include<atlimage.h>CImage类读取图⽚CImage.Load("src.bmp");CImage类保存图⽚CImage.Save("dst.jpg");⼀.CImage类实例拷贝数据到另外⼀个CImage类实例bool ImageCopy(const CImage &srcImage, CImage &destImage){int i, j;//循环变量if (srcImage.IsNull())return FALSE;//源图像参数BYTE* srcPtr = (BYTE*)srcImage.GetBits();int srcBitsCount = srcImage.GetBPP();int srcWidth = srcImage.GetWidth();int srcHeight = srcImage.GetHeight();int srcPitch = srcImage.GetPitch();//销毁原有图像if (!destImage.IsNull()){destImage.Destroy();}//创建CImage类新图像并分配内存if (srcBitsCount == 32) //⽀持alpha通道{destImage.Create(srcWidth, srcHeight, srcBitsCount, 1);}else{destImage.Create(srcWidth, srcHeight, srcBitsCount, 0);}//加载调⾊板if (srcBitsCount <= 8 && srcImage.IsIndexed())//需要调⾊板{RGBQUAD pal[256];int nColors = srcImage.GetMaxColorTableEntries();if (nColors>0){srcImage.GetColorTable(0, nColors, pal);destImage.SetColorTable(0, nColors, pal);//复制调⾊板程序}}//⽬标图像参数BYTE *destPtr = (BYTE*)destImage.GetBits();int destPitch = destImage.GetPitch();//复制图像数据memcpy(destPtr + i*destPitch, srcPtr + i*srcPitch, abs(srcPitch));}return TRUE;}⼆.CImage类实例处理图像间的腐蚀运算//腐蚀运算//width:图像宽;height:图像⾼;矩形掩膜的边长(2*r+1)void erosion(CImage image, int width, int height, int r) {int i, j, m, n;int flag;//unsigned char * pBuff = tempBuff;CImage Buff;ImageCopy(image, Buff);//dataCopy(image, pBuff, width, height);byte *pImg = (byte *)image.GetBits();byte *pBuff = (byte *)Buff.GetBits();int step = image.GetPitch();//int height = image.GetHeight();//int width = image.GetWidth();for (i = r; i<height - r; i++){for (j = r; j<width - r; j++){flag = 1;for (m = i - r; m <= i + r; m++){for (n = j - r; n <= j + r; n++){//if (!pBuff[i*width + j] || !pBuff[m*width + n])if (!*(pBuff + i*step + j) || !*(pBuff + m*step + n)){flag = 0;break;}}}if (flag == 0){*(pImg + i*step + j) = 0;}else{*(pImg + i*step + j) = 255;}}}}三.CImage类实例处理图像间的膨胀运算//膨胀运算//width:图像宽;height:图像⾼;矩形掩膜的边长(2*r+1)void diate(CImage image, int width, int height, int r){int i, j, m, n;int flag;//unsigned char * pBuff = tempBuff;CImage Buff;ImageCopy(image, Buff);//dataCopy(image, pBuff, width, height);byte *pImg = (byte *)image.GetBits();byte *pBuff = (byte *)Buff.GetBits();int step = image.GetPitch();//int height = image.GetHeight();//int width = image.GetWidth();//dataCopy(image, pBuff, width, height);for (i = r; i<height - r; i++){flag = 1;for (m = i - r; m <= i + r; m++){for (n = j - r; n <= j + r; n++){if (255 == *(pBuff + i*step + j) || 255 == *(pBuff + m*step + n)){flag = 0;break;}}}if (flag == 0){*(pImg + i*step + j) = 255;}else{*(pImg + i*step + j) = 0;}}}}四.CImage类实例处理图⽚遍历赋值操作byte *pImg = (byte *)imgSrc.GetBits();int step = imgSrc.GetPitch();int height = imgSrc.GetHeight();int width = imgSrc.GetWidth();int sum = 0;unsigned char val = 0;//初始化for (int i = 0; i<maxY; i++)for (int j = 0; j<maxX; j++)*(pDstImg + i*step + j) = 0;五.⽤CImage类实例遍历⽣成⼿指静脉边缘图#define mlen 9 //模板长度//加长扩展的⽔平边缘检测模板int upperEdgeOperator[mlen * 3] ={-1, -1, -1, -1, -1, -1, -1, -1, -1,0, 0, 0, 0, 0, 0, 0, 0, 0,1, 1, 1, 1, 1, 1, 1, 1, 1};int lowerEdgeOperator[mlen * 3] ={1, 1, 1, 1, 1, 1, 1, 1, 1,0, 0, 0, 0, 0, 0, 0, 0, 0,-1, -1, -1, -1, -1, -1, -1, -1, -1};int findEdgesHorizontal(CImage& imgSrc, CImage& imgDst){int maxY = imgSrc.GetHeight();int maxX = imgSrc.GetWidth();if (!imgDst.IsNull()){imgDst.Destroy();}imgDst.Create(maxX, maxY, 8, 0);//图像⼤⼩与imgSrc相同,每个像素占1字节if (imgDst.IsNull())return FALSE;byte *pImg = (byte *)imgSrc.GetBits();byte *pDstImg = (byte *)imgDst.GetBits();int step = imgSrc.GetPitch();int height = imgSrc.GetHeight();int width = imgSrc.GetWidth();int sum = 0;for (int j = 0; j<maxX; j++)*(pDstImg + i*step + j) = 0;//找上边缘for (int i = 1; i <maxY / 2 - 1; i++){for (int j = 4; j < maxX - 4; j++){sum = 0;for (int m = -1; m <= 1; m++){for (int n = -mlen / 2; n <= mlen / 2; n++){sum += *(pImg + (i + m)*step + (j + n))*upperEdgeOperator[(m + 1)*mlen + (n + mlen / 2)]; }}sum = sum < 0 ? 0 : sum;sum = sum > 255 ? 255 : sum;val = unsigned char(sum);*(pDstImg + i*step + j) = val;}}//找下边缘for (int i = maxY / 2 + 1; i <maxY - 1; i++){for (int j = 4; j < maxX - 4; j++){sum = 0;for (int m = -1; m <= 1; m++){for (int n = -mlen / 2; n <= mlen / 2; n++){sum += *(pImg + (i + m)*step + (j + n))*upperEdgeOperator[(m + 1)*mlen + (n + mlen / 2)];; }}sum = sum < 0 ? 0 : sum;sum = sum > 255 ? 255 : sum;val = unsigned char(sum);*(pDstImg + i*step + j) = val;}}return TRUE;}六.CImage图像类实例将RGB图转为灰度(gary)图BOOL ImageToGray(CImage& imgSrc, CImage& imgDst){int maxY = imgSrc.GetHeight();int maxX = imgSrc.GetWidth();if (!imgDst.IsNull()){imgDst.Destroy();}imgDst.Create(maxX, maxY, 8, 0);//图像⼤⼩与imgSrc相同,每个像素占1字节if (imgDst.IsNull())return FALSE;//为imgDst构造256阶灰度调⾊表RGBQUAD ColorTab[256];for (int i = 0; i<256; i++){ColorTab[i].rgbBlue = ColorTab[i].rgbGreen = ColorTab[i].rgbRed = i;}imgDst.SetColorTable(0, 256, ColorTab);byte* pDataSrc = (byte*)imgSrc.GetBits(); //获取指向图像数据的指针byte* pDataDst = (byte*)imgDst.GetBits();int pitchSrc = imgSrc.GetPitch(); //获取每⾏图像占⽤的字节数 +:top-down;-:bottom-up DIBint pitchDst = imgDst.GetPitch();int bitCountSrc = imgSrc.GetBPP() / 8; // 获取每个像素占⽤的字节数int bitCountDst = imgDst.GetBPP() / 8;if ((bitCountSrc != 3) || (bitCountDst != 1))return FALSE;int tmpR, tmpG, tmpB, avg;for (int i = 0; i<maxX; i++){tmpR = *(pDataSrc + pitchSrc*j + i*bitCountSrc);tmpG = *(pDataSrc + pitchSrc*j + i*bitCountSrc + 1);tmpB = *(pDataSrc + pitchSrc*j + i*bitCountSrc + 2);avg = (int)(tmpR + tmpG + tmpB) / 3;*(pDataDst + pitchDst*j + i*bitCountDst) = avg;}}return TRUE;}七.CImage类转opencv Mat类以及Mat类转CImage类#include "stdafx.h"#include <opencv2/opencv.hpp>#include "CimgMat.h"void CimgMat::MatToCImage(Mat& mat, CImage& cimage){if (0 == mat.total()){return;}int nChannels = mat.channels();if ((1 != nChannels) && (3 != nChannels)){return;}int nWidth = mat.cols;int nHeight = mat.rows;//重建cimagecimage.Destroy();cimage.Create(nWidth, nHeight, 8 * nChannels);//拷贝数据uchar* pucRow; //指向数据区的⾏指针uchar* pucImage = (uchar*)cimage.GetBits(); //指向数据区的指针int nStep = cimage.GetPitch(); //每⾏的字节数,注意这个返回值有正有负if (1 == nChannels) //对于单通道的图像需要初始化调⾊板{RGBQUAD* rgbquadColorTable;int nMaxColors = 256;rgbquadColorTable = new RGBQUAD[nMaxColors];cimage.GetColorTable(0, nMaxColors, rgbquadColorTable);for (int nColor = 0; nColor < nMaxColors; nColor++){rgbquadColorTable[nColor].rgbBlue = (uchar)nColor;rgbquadColorTable[nColor].rgbGreen = (uchar)nColor;rgbquadColorTable[nColor].rgbRed = (uchar)nColor;}cimage.SetColorTable(0, nMaxColors, rgbquadColorTable);delete[]rgbquadColorTable;}for (int nRow = 0; nRow < nHeight; nRow++){pucRow = (mat.ptr<uchar>(nRow));for (int nCol = 0; nCol < nWidth; nCol++){if (1 == nChannels){*(pucImage + nRow * nStep + nCol) = pucRow[nCol];}else if (3 == nChannels){for (int nCha = 0; nCha < 3; nCha++){*(pucImage + nRow * nStep + nCol * 3 + nCha) = pucRow[nCol * 3 + nCha];}}}}void CimgMat::CImageToMat(CImage& cimage, Mat& mat){if (true == cimage.IsNull()){return;}int nChannels = cimage.GetBPP() / 8;if ((1 != nChannels) && (3 != nChannels)){return;}int nWidth = cimage.GetWidth();int nHeight = cimage.GetHeight();//重建matif (1 == nChannels){mat.create(nHeight, nWidth, CV_8UC1);}else if (3 == nChannels){mat.create(nHeight, nWidth, CV_8UC3);}//拷贝数据uchar* pucRow; //指向数据区的⾏指针uchar* pucImage = (uchar*)cimage.GetBits(); //指向数据区的指针int nStep = cimage.GetPitch(); //每⾏的字节数,注意这个返回值有正有负for (int nRow = 0; nRow < nHeight; nRow++){pucRow = (mat.ptr<uchar>(nRow));for (int nCol = 0; nCol < nWidth; nCol++){if (1 == nChannels){pucRow[nCol] = *(pucImage + nRow * nStep + nCol);}else if (3 == nChannels){for (int nCha = 0; nCha < 3; nCha++){pucRow[nCol * 3 + nCha] = *(pucImage + nRow * nStep + nCol * 3 + nCha); }}}}}⼋.纯图像数据赋值给CImage后的初始化,并且写⼊调⾊板bool InitalImage(CImage &image, int width, int height){if (image.IsNull())image.Create(width, height, 8);else{if (width <= 0 || height <= 0)return false;else if (image.GetHeight() == width && image.GetWidth() == height)return true;else{image.Destroy();image.Create(width, height, 8);}}//写⼊调⾊板image.GetColorTable(0, 256, ColorTable);for (int i = 0; i < 256; i++){ColorTable[i].rgbBlue = (BYTE)i;ColorTable[i].rgbGreen = (BYTE)i;ColorTable[i].rgbRed = (BYTE)i;}image.SetColorTable(0, 256, ColorTable);return true;}九.根据MFC控件⼤⼩CImage类实例图⽚显⽰if(m_image2.IsNull()) //判断有⽆图像return;// 取得客户区尺⼨CRect zcRect;GetDlgItem(IDC_STATIC_PIC2)->GetClientRect(&zcRect);// 将图像显⽰在界⾯之上m_image2.Draw(GetDlgItem(IDC_STATIC_PIC2)->GetDC()->m_hDC, zcRect.left,zcRect.top,zcRect.Width(),zcRect.Height());⼗.根据CImage类实例图⽚调整控件⼤⼩显⽰图⽚if(m_image1.IsNull())return;// 将整控件调整为与图像同⼀尺⼨GetDlgItem(IDC_STATIC_PIC)->SetWindowPos(NULL,0,0,m_image1.GetWidth(), m_image1.GetHeight(),SWP_NOMOVE);CRect zcRect;GetDlgItem(IDC_STATIC_PIC)->GetClientRect(&zcRect);m_image1.Draw(GetDlgItem(IDC_STATIC_PIC)->GetDC()->m_hDC, zcRect.left,zcRect.top,zcRect.Width(),zcRect.Height());⼗⼀.CImage类与CBitmap转换CImage nImage;nImage.Load(imgFilePath);HBITMAP hBitmap=nImage.Detach(); // 获得位图句柄⽤以转换// 转换⽅式⼀:CBitmap bmp;bmp.DeleteObject();bmp.Attach(hBitmap); // 转换为CBitmap对象// 转换⽅式⼆:CBitmap *pBitmap=CBitmap::FromHandle(nImage.m_hBitmap);⼗⼆.CImage类实例实现图像⼆值化void imgBinary(CImage image, int imgW, int imgH, int threshold){int index = 0;byte *pImg = (byte *)image.GetBits();int step = image.GetPitch();int height = image.GetHeight();int width = image.GetWidth();for (i = 0; i<height*width; i++){*(pImg + index) = *(pImg + index)>threshold ? 255 : 0;index++;}}⼗三.CImage实现⾃⼰的argmax函数----求图像⼀定⾼度区域中某⼀列遇到的第⼀个最⼤像素值的坐标并返回int argmax(CImage &image,int Top,int Bottom,int x){int max = 0;int tem;int pos = 0;byte *pImg = (byte *)image.GetBits();int step = image.GetPitch();int height = image.GetHeight();int width = image.GetWidth();if (Top > 0 && Top < height && Bottom > 0 && Bottom < height && x > 0 && x < width){for (int i = Top; i < Bottom; ++i){tem = *(pImg + i*step + x);if (tem > max){max = tem;pos = i;}}return pos;}else{return FALSE;}}⼗四.CImage类创建指定长宽图像并初始化调⾊板bool InitalImage(CImage &image, int width, int height){if (image.IsNull())image.Create(width, height, 8);else{if (width <= 0 || height <= 0)return false;else if (image.GetHeight() == width && image.GetWidth() == height)return true;else{image.Destroy();image.Create(width, height, 8);}}//写⼊调⾊板for (int i = 0; i < 256; i++){ColorTable[i].rgbBlue = (BYTE)i;ColorTable[i].rgbGreen = (BYTE)i;ColorTable[i].rgbRed = (BYTE)i;}image.SetColorTable(0, 256, ColorTable);return true;}⼗五.将存放在⼀维指针数组⾥的图像数据赋值给CImage类实例void LoadImageData(CImage &image, unsigned char * data){if (data == nullptr)return;byte *pS;byte *pImg = (byte *)image.GetBits();int step = image.GetPitch();int height = image.GetHeight();int width = image.GetWidth();for (int i = 0; i < image.GetHeight(); ++i){pS = data + i * width;for (int j = 0; j < image.GetWidth(); ++j){*(pImg + i*step + j) = pS[j];}}}⼗六.CImage类⾃⼰实现图⽚的裁剪//裁剪roi区域void RoiCut(CImage &image, CImage &roiImg, int heightTop,int heightDown,int widthBegin,int widthEnd) {InitalImage(roiImg, heightDown - heightTop + 1, widthBegin - widthEnd + 1);byte *pImg = (byte *)image.GetBits();byte *pRoi = (byte *)roiImg.GetBits();int step = image.GetPitch();int height = image.GetHeight();int width = image.GetWidth();int index = 0;for (int i = heightTop; i < heightDown; i++){for (int j = widthBegin; j < widthEnd; j++){*(pRoi + index) = *(pImg + i*step + j);index++;}}}⼗七.CImage类截取电脑屏幕图⽚,也就是屏幕截图的意思#include"iostream"#include<atlimage.h>int main(){HDC hDCScreen = ::GetDC(NULL);//⾸先获取到屏幕的句柄int nBitPerPixel = GetDeviceCaps(hDCScreen, BITSPIXEL);//获取到每个像素的bit数⽬int nWidthScreen = GetDeviceCaps(hDCScreen, HORZRES);int nHeightScreen = GetDeviceCaps(hDCScreen, VERTRES);//Create实例化CImage,使得其内部的画布⼤⼩与屏幕⼀致m_MyImage.Create(nWidthScreen, nHeightScreen, nBitPerPixel);//获取到CImage的 HDC,但是需要⼿动ReleaseDC操作,下⾯是MSDN的说明//Because only one bitmap can be selected into a device context at a time,//you must call ReleaseDC for each call to GetDC.HDC hDCImg = m_MyImage.GetDC();//使⽤bitblt 将屏幕的DC画布上的内容拷贝到CImage上BitBlt(hDCImg, 0, 0, nWidthScreen, nHeightScreen, hDCScreen, 0, 0, SRCCOPY);//直接保存吧m_MyImage.Save("C:\\test.bmp", Gdiplus::ImageFormatBMP);m_MyImage.Save("C:\\test.png", Gdiplus::ImageFormatPNG);m_MyImage.Save("C:\\test.jpeg", Gdiplus::ImageFormatJPEG);//前⾯调⽤了GetDC所以需要调⽤ReleaseDC释放掉//详情请参见MSDNm_MyImage.ReleaseDC();return0;}⼗⼋.CImage转为CBitmap以及CBitmap转CImage#include<atlimage.h>#include<afxwin.h>void cimageToCbitmap(CImage & srcImg, CBitmap & dstImg){HBITMAP hbmp = (HBITMAP)srcImg.operator HBITMAP();dstImg.DeleteObject();dstImg.Attach(hbmp);}void cbitmapToCimage(CBitmap & srcImg, CImage & dstImg){HBITMAP hbmp = (HBITMAP)srcImg.GetSafeHandle();if (!dstImg.IsNull()) //Attach前,必须确认img中⽆位图,否则会弹出异常,但忽略也可继续运⾏dstImg.Destroy();dstImg.Attach(hbmp);//另注:原对象中的位图改变后,所有Attach到这个对象的对象需从新Attach⼀次,否则图像为初始化的颜⾊或⿊⾊!}⼗⼋.CImage类实现图形旋转90度(通过计算旋转矩阵之后的位置直接映射过去)CImage * srcImg = new CImage();srcImg->Load(strPicName);BYTE* srcPtr = (BYTE*)srcImg->GetBits();int srcBitsCount = srcImg->GetBPP();int srcWidth = srcImg->GetWidth();int srcHeight = srcImg->GetHeight();int srcPitch = srcImg->GetPitch();//删除原始图⽚DeleteFile(strPicName);//创建新图像CImage * dstImg = new CImage();dstImg->Create(1200, 800, srcBitsCount, 0);//加载调⾊板if (srcBitsCount <= 8 && srcImg->IsIndexed())//需要调⾊板{RGBQUAD pal[256];int nColors = srcImg->GetMaxColorTableEntries();if (nColors > 0){srcImg->GetColorTable(0, nColors, pal);}//⽬标图像参数BYTE *destPtr = (BYTE*)dstImg->GetBits();int destPitch = dstImg->GetPitch();int width = dstImg->GetWidth();int height = dstImg->GetHeight();//复制图像数据for (int i = 0; i < height; i++){for (int j = 0; j < width; j++){if(srcBitsCount == 24){*(destPtr + (799 - j)*destPitch + 3 * i) = *(srcPtr + i * srcPitch + 3 * j);//⽬的图⽚的第⼀列*(destPtr + (799 - j)*destPitch + 3 * i + 1) = *(srcPtr + i * srcPitch + 3 * j + 1);*(destPtr + (799 - j)*destPitch + 3 * i + 2) = *(srcPtr + i * srcPitch + 3 * j + 2);}else if(srcBitsCount == 8){*(destPtr + (799 - j)*destPitch + i) = *(srcPtr + i * srcPitch + j);//⽬的图⽚的第⼀列}}}//保存新图像dstImg->Save(strPicName);delete srcImg;delete dstImg;还有⼀些操作⽐较⿇烦这⾥就先不写了,后⾯想到什么在写把嘿嘿。