VS2008 + Opencv2.1 读取图片像素输出至Excel文件

系统环境:

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以上可以看到全部数值。

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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] = { 00};
     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文件成功");
}


输出的文件如图:

【前言】 工作或学习中可能需要实现基于VC读\写Excel文件的功能,本人最近也遇到了该问题。中间虽经波折,但是最终还是找到了解决问题的办法。 在此跟大家分享,希望对跟我同样迷茫过的同学们有所帮助。 1、程序功能 1)打开一个excel文件; 2)显示到CListCtrl上; 3)新建一个Excel文件。 以上均在对话框中实现。 2、平台 VC++2010 3、实现方法 常用的Excel打开方式有两种 1)通过数据库打开; 2)OLE方式打开。 由于方式1)操作繁琐,经常出现莫名的错误,这里选用方式2. 4、准备步骤 首先新建一个Dialog窗体程序,添加list control和两个按钮 1)将ExcelLib文件夹拷贝到程序目录下; 2)将Export2Excel.h,Export2Excel.cpp两个文件添加到项目; 3)包含头文件,#include "ExcelLib/Export2Excel.h" 通过以上步骤在程序中引入了可以读取Excle文件的CExport2Excel类; 5、打开excel文件 通过按钮点击打开 void CExcelTestDlg::OnBnClickedButtonOpenExcel() { //获取文件路径 CFileDialog* lpszOpenFile; CString szGetName; lpszOpenFile = new CFileDialog(TRUE,"","",OFN_FILEMUSTEXIST|OFN_HIDEREADONLY,"Excel File(*.xlsx;*.xls)|*.xls;*.xlsx",NULL); if (lpszOpenFile->DoModal()==IDOK) { szGetName = lpszOpenFile->GetPathName(); SetWindowText(szGetName); delete lpszOpenFile; } else return; //打开文件 //文件中包含多个sheet时,默认打开第一个sheet CExport2Excel Excel_example; Excel_example.OpenExcel(szGetName); //获取sheet个数 int iSheetNum = Excel_example.GetSheetsNumber(); //获取已使用表格行列数 int iRows = Excel_example.GetRowCount(); int iCols = Excel_example.GetColCount(); //获取单元格的内容 CString cs_temp = Excel_example.GetText(1,1); //AfxMessageBox(cs_temp); //List control上显示 //获取工作表列名(第一行) CStringArray m_HeadName; m_HeadName.Add(_T("ID")); for (int i=1;iGetItemCount()>0) { m_list.DeleteColumn(0); } //初始化ClistCtrl,加入列名 InitList(m_list,m_HeadName); //填入内容 //第一行是标题,所以从第2行开始 CString num; int pos; for (int row = 2;row<=iRows; row++) { pos = m_list.GetItemCount(); num.Format(_T("%d"),pos +1); m_list.InsertItem(pos,num); for (int colum=1;columDoModal()==IDOK) { szGetName = lpszOpenFile->GetPathName(); SetWindowText(szGetName); delete lpszOpenFile; } else return; //文件全名称 CString csFileName = szGetName; //需要添加的两个sheet的名称 CString csSheetName = "newSheet"; CString csSheetName2 = "newSheet2"; // 新建一个excel文件,自己写入文字 CExport2Excel Excel_example; //新建excel文件 Excel_example.CreateExcel(csFileName); //添加sheet,新加的sheet在前,也就是序号为1 Excel_example.CreateSheet(csSheetName); Excel_example.CreateSheet(csSheetName2); //操作最开始添加的sheet:(newSheet) Excel_example.SetSheet(2); //添加表头 Excel_example.WriteHeader(1,"第一列"); Excel_example.WriteHeader(2,"第二列"); //添加核心数据 Excel_example.WriteData(1,1,"数据1"); Excel_example.WriteData(1,2,"数据2"); //保存文件 Excel_example.Save(); //关闭文件 Excel_example.Close(); } 7、注意事项 1)一般单个Excel文件包含多个sheet,程序默认打开第一个2)指定操作sheet,使用Excel_example.SetSheet(2)函数; 3)打开文件时最左侧的sheet序号为1,新建excel时最新添加的sheet序号为1. 【后记】 本程序主要基于网络优快云中---“Excel封装库V2.0”---完成,下载地址是:http://download.csdn.net/detail/yeah2000/3576494,在此表示感谢!同时, 1)在其基础上作了小改动,改正了几个小错误,添加了几个小接口; 2)添加了如何使用的例子,原程序是没有的; 3)详细的注释 发现不足之处,还请大家多多指教!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值