查看: 1701|回复: 0
打印 上一主题 下一主题

WinCE中解决“图片采集及压缩”问题的开发历程

[复制链接]
跳转到指定楼层
沙发
发表于 2015-4-23 16:27:56 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

已有条件:

  • 设备:WinCE工控板一个,LCD液晶屏一个,摄像头一个,必要的接线。
  • 开发环境:开发程序的相关软件VS2005,开发SDK,工控板对应的DLL驱动

要求:

   让WinCE工控板上的摄像头拍照,然后将图片数据通过GPRS发送到指定的主机数据库中。

   GPRS发送模块是另外一个同学开发的。所以,我要做的就是拍照,并提取出图片数据,然后将数据接口给 开发GPRS的那位同学,后面要做的就不是我的事情了。

   然后这个过程就开始了,一个曲曲折折的过程啊。


第一阶段:准备用C#进行WinCE开发

    GPRS模块已经用C#开发好了,可以无线发送数据,然后就等着图片数据接口了。然后我就开始担当获取图片数据的任务了。此时,我的基础基本上是零,连WinCE到底是个什么样子的东西都不是太清楚。只知道好像很多能在XP下运行的C#函数在WinCE下都不能用了。

目前的学习资料仅限于致远公司提供的一个光盘里面的一些演示程序。

    关于摄像头的演示程序只有VS2005环境开发的,基于C++的演示程序。想变成C#都不行啊。虽然那个DLL驱动可以供C#调用,但是看那个驱动DLL的方法,在使用提取数据的那个函数的时候,必需要设置内核模式,而这个只能用C++来做,因为需要引用一个头文件中的某个函数,显然C#是没有头文件这个概念的。

    于是这个时候的计划就是,用C++来写DLL程序,将致远公司提供的硬件层的驱动再封装一遍(把那个内核模式的设置函数封装进去),然后提取出图片数据,然后再用C#调用得到图片数据,保存到一个BYTE数组中,然后通过GPRS发送。

    阶段总结:知道C#在比较底层的开发方面确实乏力,所以放弃C#转投C++。


第二阶段:用C#程序和C++的DLL产生数据交互

    用C++写的DLL可以提取到图片数据了,而且已经在工控板上的存储盘上保存了BMP位图文件。这个时候,想办法用C#主程序来承接这些数据,这个时候,就直接调用DLL中的函数,生成文件,然后此函数返回文件路径,C#程序中得到了文件路径,然后再读取文件,然后GPRS发送位图数据流。

阶段总结:用上面的方案虽然可以达到目标,但是还存在如下巨大缺陷。

缺点:

  • 1. 此时提取的图片数据为150K的RGB565编码方式的位图文件,发送一次,耗的时间太长了,而且耗流量。每拍一次图片就要往存储盘里面写一次文件,长期下来,反复擦写存储盘,必然会带来硬件的快速损耗。
  • 2. 每拍一次图片就要往存储盘里面写一次文件,长期下来,反复擦写存储盘,必然会带来硬件的快速损耗。

要求:

1. 对图片进行压缩,最好是用JPEG格式的。

2. 将图片的处理都放在内存中处理,最后也是在内存中将数据流传递给C#主程序。


第三阶段:压缩BMP图片到JPEG图片

    在XP下用C#可以直接读BMP文件,然后构造一个Bitmap类,然后有个成员函数,直接一步保存为你想要的格式,比如保存成JPG,可以从150K压缩到10K,而且人眼基本看不出差别。本来以为可以很快搞定的,结果发现,同样的代码在XP环境下可以运行,在WinCE平台下就不行了。花了几天做了好多好多的试验,才最后完全确实,这种方法走不通。将XP环境下的位图文件和代码全部复制到开发板中,但是就是不能得到和XP下同样的运行结果,在程序读取文件并构造位图对象的时候,在WinCE下位出现异常。可能是因为WinCE和WinXP下的.NET框架类的数据结构不太一样吧。这个很沮丧啊。一下不知道怎么办了。

    然后研究致远公司提供的代码示例,了解BMP位图的文件结构,从存储内容上进行分析。想从最底层上对图片进行逐字节的运算和操作。但是后来想到Jpeg的压缩算法实在太复杂,最好是有现有的类函数提供编码解码压缩。

    最后到网上找到一篇关于WinCE下的BMP转JPG的文章,用的是IImage,IImageFactory接口。但是网上谈到这种转码压缩方法的时候,都是说RGB555的可以,但是RGB565的不行。刚好我的图片就是RGB565的,我不信,然后将网上给的代码放到程序中试验,结果果然不行,很泄气。想找个RGB555的图片,但是找不到,网上都说用Photoshop可以生成,我也没有搞成功。后来在胡博士的提示下:RGB565和RGB555编码方式可以通过数据位运算进行转换,转换成功后,再用IImage试试。后来我参考网上下载的一篇关于BMP位图文件结构的文档,然后对RGB565成功转码成RGB555的位图文件。然后很顺利,RGB555可以用IImageFactory转码成jpg格式。150K的位图可以压缩到10K。



第三阶段:在内存中实现图片压缩

    在IImageFactory中,有个函数,可以直接将jpg的编码结果保存在IStream流中,当时自己很高兴,有个这现成的函数,但后来发现,数据根本就就没有保存进去。然后又开始了对IStream的使用方法进行研究,发现,经过那个函数后,IStream的大小变成了图片的大小 ,但是却读不出数据,我以为是因为ISream作为传出参数,但是不是传的指针的地址,可能有问题。后来又是在胡博士的提醒下,原来是因为我的ISream在赋值完毕后,没有将偏移指针移到首位,导致后面没有成功读出数据。这个时候才知道,原来ISream只是一个接口类,也就是所谓的虚类,必需要和一个内存进行绑定或者进行一次实例化。属于比较特殊的指针,在作为传出参数使用的时候,不需要使用指针的地址作为传出参数就可以达到传出数据的功能。


第四阶段:在C#主程序中对DLL数据进行承接

    因为最终我要做出的效果就是,能够在C#程序中提供一个数据接口,也就是我能够给GPRS模块一个在C#环境下的byte数组。这个时候又遇到一点小麻烦问题了,就是在C++中有指针和动态内存分配,但是在C#里面“好像”没有。这个时候又遇到麻烦了。网上普遍说的,C++的DLL向C#传出字符数组char*时,在C#程序中对应的数据类型是StringBuilder类。当时看到这个觉得比较高兴,也在XP环境下测试成功,主要就是一个引用DLL的时候,设置一个传值的编码方式,一般默认为UniCode,改成Ansi就可以了。

    以为可以直接往WinCE下移植了,但是移植的时候,发现WinCE下的C#调用DLL的时候,设置传值编码方式的选项中,没有Ansi选项,于是这就表示不能使用StringBuilder来进行数据承接了,否则承接也是乱码。

    最后到网上找到了一个C#中的一个可以“模拟”指针的方法IntPtr,可以实现非托管内存数据和托管内存数据之间的读取的转换,也就是所谓的内存操作。这个可以和C++中的动态分配内存相对应起来。于是这个技术问题解决后,BMP转JPG才算在技术上完全走通了。


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 加入中科因仑

本版积分规则

快速回复 返回顶部 返回列表