系统环境:
win 7 + VS2008 + Opencv2.1 + Excel 2010
思路:先通过Opencv库函数读取图片存储至IplImage结构体中,接着通过OLE/COM方式实现对excel文件的写入,对多个单元格的读写操作可以通过CRange中以下两个成员函数来完成。VARIANT get_Value2(); void put_Value2(VARIANT& newValue); 其中,输入参数newValue只要输入一个二维数组,即可实现向Excel中一次写入多个单元格的值。前提是程序能够通过识别图片的大小而设置二维数组的起始点,举例来说,Excel 2010 里一共有1048576行, XFD列,X是26个字母中第24个字母,F是第6个,D是第4个,因此XFD对应的是 24*26*26+6*26+4=16384,图片的高度即Excel中的行数,将图片的宽度对26进行取商求余的操作可得到由字母表示的对应的列数。
操作过程:
1、新建C++工程
新建自己的C++工程。
2、添加Excel类库
在工程名上右键,选择Add---Class,选择MFC Class From TypeLib,如图:
然后添加如图的几个类。
3. 修改头文件
进入刚添加进来的几个类头文件中将#import开头的这句注掉
// 从类型库向导中用“添加类”创建的计算机生成的 IDispatch 包装类 //#import "D:\\Program Files\\Microsoft Office\\Office14\\EXCEL.EXE" no_namespace // CApplication 包装类
4、修改提示的错误
将修改过的工程编译一下,出现如下错误:
双击提示,在DialogBox()前加一下划线即可。
5、添加头文件
在使用导出功能的文件中添加头文件(包括opencv头文件):
#include "CApplication.h" #include "CFont0.h" #include "CRange.h" #include "CWorkbook.h" #include "CWorkbooks.h" #include "CWorksheet.h" #include "CWorksheets.h" #include "afxdisp.h" #include "comutil.h" #include <cv.h> #include <cxcore.h> #include <highgui.h> #include <stdlib.h>
6、使用Excel类库提供的函数将需要导出的数据导出为.xlsx文件
经过以上几步,现在可以使用Excel类库提供的函数导出数据了。与网上许多文章相对比一下,导出流程一样。只不过类库函数有所改变。函数名由Get改为get_,Set改为put_,代码如下。
说明:
1.pimg的类型 是 IplImage*,opencv库函数读取进来的图片存储在结构体 IplImage中。
2.由于初始的应用是想读出灰度图的像素值,故只输出了一个通道的值,其余两个通道可以照推。
3.如果读取的图片宽度大于256,则输出的excel文件若使用wps打开只能看到最大列为256,excel2007以上可以看到全部数值。
void CSpectrumProDoc::OnExportexcel()
{
// TODO: 在此添加命令处理程序代码
CString sPath = _T("D:\\Image\\"); //注意不能是c盘的路径,没有权限写入
CTime time = CTime::GetCurrentTime();
CString sfilename ;
sfilename.Format("E%02d%02d%02d%02d%02d", time.GetMonth(), time.GetDay(),
time.GetHour(), time.GetMinute(), time.GetSecond());
/* 生成的文件若使用wps打开则只能看到最大列为256 */
CString strFile = sPath + sfilename + _T(".xlsx");
CApplication app;
CWorkbook book;
CWorkbooks books;
CWorksheet sheet;
CWorksheets sheets;
CRange range;
CFont0 font;
CRange cols;
LPDISPATCH lpDisp = NULL;
COleVariant covTrue((short)TRUE);
COleVariant covFalse((short)TRUE);
COleVariant covOptional((long)DISP_E_PARAMNOTFOUND, VT_ERROR);
if (!app.CreateDispatch("Excel.Application"))
{
AfxMessageBox("未能创建Excel应用程序!");
return ;
}
books = app.get_Workbooks();
book = books.Add(covOptional);
sheets = book.get_Worksheets();
sheet = sheets.get_Item(COleVariant((short)1));
// range = sheet.get_Range(COleVariant("A1"), COleVariant("A1"));
// range.put_Value2(COleVariant("hello excel!"));
//************************************************************
CString height;
if (pimg->height > 1000)
{
char buf1[5] = {0}; //注意末尾需要以'\0'结尾
height = itoa(pimg->height, buf1, 10);
}
else
{
char buf1[4] = {0};
height = itoa(pimg->height, buf1, 10);
}
char buf2[4] = {0}; //注意末尾需要以'\0'结尾
buf2[0] = pimg->width / (26 * 26) + 64;
CString dest;
if (buf2[0] == 64)
{
buf2[1] = pimg->width / 26 + 64;
buf2[2] = pimg->width % 26 + 64;
char buf3[3] = {buf2[1], buf2[2], buf2[3]};
CString temp;
temp.Format("%s", buf3);
dest = temp + height;
}
else
{
buf2[1] = (pimg->width - (buf2[0] - 64) * 26 * 26) / 26 + 64;
buf2[2] = (pimg->width - (buf2[0] - 64) * 26 * 26) % 26 + 64;
CString temp;
temp.Format("%s", buf2);
dest = temp + height;
}
/*向Sheet中写入多个单元格,规模由读取的图片决定 */
lpDisp = sheet.get_Range(_variant_t("A1"), _variant_t(dest));
range.AttachDispatch(lpDisp);
VARTYPE vt = VT_I4; /*数组元素的类型,long */
SAFEARRAYBOUND sabWrite[2]; /*用于定义数组的维数和下标的起始值*/
sabWrite[0].cElements = pimg->width;
sabWrite[0].lLbound = 0;
sabWrite[1].cElements = pimg->height;
sabWrite[1].lLbound = 0;
COleSafeArray olesaWrite;
olesaWrite.Create(vt, sizeof(sabWrite) / sizeof(SAFEARRAYBOUND), sabWrite);
/*通过指向数组的指针来对二维数组的元素进行间接赋值*/
long (*pArray)[2] = NULL;
olesaWrite.AccessData((void **)&pArray);
memset(pArray, 0, sabWrite[0].cElements * sabWrite[1].cElements * sizeof(long));
/*释放指向数组的指针*/
olesaWrite.UnaccessData();
pArray = NULL;
/*对二维数组的元素进行逐个赋值*/
long index[2] = {0, 0};
long lFirstLBound = 0;
long lFirstUBound = 0;
long lSecondLBound = 0;
long lSecondUBound = 0;
olesaWrite.GetLBound(1, &lFirstLBound);
olesaWrite.GetUBound(1, &lFirstUBound);
olesaWrite.GetLBound(2, &lSecondLBound);
olesaWrite.GetUBound(2, &lSecondUBound);
for (long i = lFirstLBound; i <= lFirstUBound; i++)
{
index[0] = i;
for (long j = lSecondLBound; j <= lSecondUBound; j++)
{
index[1] = j;
/* 经测试,24位深图片为8bit 3通道,灰度图(RGB都相等) */
/* 如果是8bit 1通道的图像 I(x,y) ~ ((uchar*)(img->imageData + img->widthStep*y))[x] */
long lElement = ((uchar *)(pimg->imageData + pimg->widthStep * j))[i * 3];
olesaWrite.PutElement(index, &lElement);
}
}
/*把ColesaWritefeArray变量转换为VARIANT,并写入到Excel表格中*/
VARIANT varWrite = (VARIANT)olesaWrite;
range.put_Value2(varWrite);
//*******************************************************************
font = range.get_Font();
font.put_Bold(COleVariant((short)TRUE));
cols = range.get_EntireColumn();
cols.AutoFit();
app.put_Visible(TRUE);
app.put_UserControl(TRUE);
book.SaveCopyAs(COleVariant(strFile));
/*
book.SaveAs(_variant_t(strFile), _variant_t((long)51),
vtMissing, vtMissing, vtMissing, vtMissing, 0, vtMissing, vtMissing, vtMissing,
vtMissing, vtMissing);
*/
book.put_Saved(TRUE);
book.ReleaseDispatch();
books.ReleaseDispatch();
app.Quit();
app.ReleaseDispatch();
AfxMessageBox("输出图像像素数据至excel文件成功");
}
输出的文件如图:
参考:
http://hfp0601.blog.163.com/blog/static/228483522011031104718762/
http://www.cnblogs.com/xianyunhe/archive/2011/09/25/2190485.html