<?xml version="1.0" standalone="yes"?>
<?xml-stylesheet type="text/xsl" href="css/rss.xslt"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>ZwqXin - 图像处理</title><link>http://www.zwqxin.com/</link><description>    一起学习OPENGL吧 - </description><generator>RainbowSoft Studio Z-Blog 1.8 Arwen Build 90619</generator><language>zh-CN</language><copyright>Copyright 2008-2010 ZwqXin. Some Rights Reserved. Theme edited from ipati. </copyright><pubDate>Fri, 10 Sep 2010 13:03:51 +0800</pubDate><item><title>摄像机标定技术 PART2</title><author>a@b.com (zwqxin)</author><link>http://www.zwqxin.com/archives/image-processing/camera-calibration-2.html</link><pubDate>Thu, 16 Jul 2009 22:47:55 +0800</pubDate><guid>http://www.zwqxin.com/archives/image-processing/camera-calibration-2.html</guid><description><![CDATA[<p>&nbsp;紧接上篇，摄像机标定。本日志在于陈述一下RAC标定的算法，事实上觉得自己在这里也是一知半解的，不过还是厚一下脸皮吧。熟悉此算法的朋友请指教（不过也许等被指教的时候我都忘记这东西了~）。&mdash;&mdash;<a href="http://www.zwqxin.com/">ZwqXin</a>.com</p><p>上篇：[<a target="_blank" href="http://www.zwqxin.com/archives/image-processing/camera-calibration-1.html">摄像机标定技术 PART1</a>]</p><p>其实算法也就是TSAI蔡先生那篇TSAI - A Versatile Camera Calibration Technique for High-Accuracy 3D Machine Vision Metrology Using Off-the-shelf TV Cameras and Lenses里写得很清楚很明白。除了算法最后一两步那个关于非线性优化的我是完全不明白他是要在干什么外，其余的也差不多是简略 - 翻译这样的样子。所以本篇文章实在无意义啊！好了当我骗稿费，这篇骗得更严重点。但好歹是总结过的，写过实验报告的，泪飘~~。</p><div><b><span style="font-size: 12pt">步骤</span></b><b><span style="font-size: 12pt">1</span></b><b><span style="font-size: 12pt">：获取摄象机硬数据</span></b></div><div><span style="font-size: 12pt">&nbsp;&nbsp;&nbsp; </span><span style="font-size: 12pt">具体来说，是</span><span style="font-size: 12pt">Ncx</span><span style="font-size: 12pt">，</span><span style="font-size: 12pt">Nfx</span><span style="font-size: 12pt">，</span><span style="font-size: 12pt">Cx</span><span style="font-size: 12pt">，</span><span style="font-size: 12pt">Cy</span><span style="font-size: 12pt">，</span><span style="font-size: 12pt">dx</span><span style="font-size: 12pt">，</span><span style="font-size: 12pt">dy</span><span style="font-size: 12pt">，</span><span style="font-size: 12pt">sx </span><span style="font-size: 12pt">七个参数。</span><span style="font-size: 12pt">Ncx</span><span style="font-size: 12pt">，</span><span style="font-size: 12pt">Nfx</span><span style="font-size: 12pt">表明了像平面像素的采样频率，简单设置为</span><span style="font-size: 12pt">1</span><span style="font-size: 12pt">，</span><span style="font-size: 12pt">sx</span><span style="font-size: 12pt">相当于误差项，也设置为</span><span style="font-size: 12pt">1</span><span style="font-size: 12pt">。</span><span style="font-size: 12pt">Cx</span><span style="font-size: 12pt">，</span><span style="font-size: 12pt">Cy</span><span style="font-size: 12pt">是像平面中心像素位置，如相机分辨率是</span><span style="font-size: 12pt">352*288</span><span style="font-size: 12pt">，则取</span><span style="font-size: 12pt">176</span><span style="font-size: 12pt">，</span><span style="font-size: 12pt">144</span><span style="font-size: 12pt">计算后可稍微调整适应。</span><span style="font-size: 12pt">dx</span><span style="font-size: 12pt">，</span><span style="font-size: 12pt">dy</span><span style="font-size: 12pt">是像素物理尺寸，我取</span><span style="font-size: 12pt">0.0054</span><span style="font-size: 12pt">。</span></div><div>&nbsp;</div><div><b><span style="font-size: 12pt">步骤</span></b><b><span style="font-size: 12pt">2</span></b><b><span style="font-size: 12pt">：计算中间数据</span></b><b><span style="font-size: 12pt">Ty^(-1)*r1, Ty^(-1)*r2, Ty^(-1)*Tx, Ty^(-1)*r4, Ty^(-1)*r5</span></b></div><div style="text-indent: 21.75pt"><span style="font-size: 12pt">根据</span><span style="font-size: 12pt">RAC</span><span style="font-size: 12pt">原理&mdash;&mdash;径向一致约束，在不考虑畸变的情况下，&ldquo;摆平&rdquo;后的世界坐标原点与摄象机坐标平面中心原点连线</span><span style="font-size: 12pt">AB</span><span style="font-size: 12pt">，以及，摄象机坐标平面中心到像平面中心连线</span><span style="font-size: 12pt">BC</span><span style="font-size: 12pt">应该会满足重合，或者</span><span style="font-size: 12pt">AB//BC</span><span style="font-size: 12pt">的条件。</span></div><div style="text-indent: 21.75pt; text-align: center">&nbsp;<a href="http://www.zwqxin.com/archives/image-processing/camera-calibration-2.html"><img alt="摄象机标定 from Tsai www.zwqxin.com" src="http://lh4.ggpht.com/_lYWT-2PnV0s/Sro9tThUniI/AAAAAAAAA78/Rmn_djy1C68/s800/Snap3090711.jpg" /></a></div><div style="text-indent: 21.75pt">&nbsp;</div><div style="text-indent: 21.75pt"><div style="text-indent: 21.75pt" align="center">&nbsp;</div><span style="font-size: 12pt">即，含外参数（视图变换矩阵参数）的中间变量的求解将完全沦为上述方程的求解，其中右边项是未知项。</span><span style="font-size: 12pt">5</span><span style="font-size: 12pt">个未知数一般来说是</span><span style="font-size: 12pt">5</span><span style="font-size: 12pt">方程求解。但</span><span style="font-size: 12pt">RAC</span><span style="font-size: 12pt">优点在于线性函数的易解，因此可取更多数据点作拟合。作为一个超定方程来精确求解。</span></div><div style="text-indent: 21.75pt">&nbsp;</div><div style="text-indent: 21.75pt"><b><span style="font-size: 12pt">步骤</span></b><b><span style="font-size: 12pt">3</span></b><b><span style="font-size: 12pt">：计算</span></b><b><span style="font-size: 12pt">Ty^2</span></b></div><div style="text-indent: 21.75pt"><span style="font-size: 12pt">根据</span><span style="font-size: 12pt">TSAI</span><span style="font-size: 12pt">论文中某条理论的解释，仿射矩阵的左上</span><span style="font-size: 12pt">3X3</span><span style="font-size: 12pt">矩阵的上</span><span style="font-size: 12pt">2X2</span><span style="font-size: 12pt">小矩阵决定了整体的一致性。</span></div><div style="text-indent: 21.75pt; text-align: center"><span style="font-size: 12pt"><a href="http://www.zwqxin.com/archives/image-processing/camera-calibration-2.html"><img alt="摄象机标定 from Tsai www.zwqxin.com" src="http://i3.6.cn/cvbnm/cf/80/59/0d487b29854718924f50f26505041427.jpg" /></a></span></div><div style="text-indent: 21.75pt">&nbsp;</div><div style="text-indent: 21.75pt"><span style="font-size: 12pt">因此当它满秩的时候，</span><span style="font-size: 12pt">Ty^2</span><span style="font-size: 12pt">的求解可按上式。非满秩的情况少有，当出现时也可按下式求解：</span></div><div style="text-indent: 21.75pt; text-align: center"><span style="font-size: 12pt"><span style="font-size: 12pt"><a href="http://www.zwqxin.com/archives/image-processing/camera-calibration-2.html"><img alt="摄象机标定 from Tsai www.zwqxin.com" src="http://i3.6.cn/cvbnm/d6/7e/9f/bffb81d455d6fe6719893fc38450bacc.jpg" /></a></span></span></div><div style="text-indent: 21.75pt"><span style="font-size: 12pt">更深入的探讨。从计算机图形学的变换矩阵知识可以知道，仿射矩阵的左上</span><span style="font-size: 12pt">3X3</span><span style="font-size: 12pt">矩阵是表征坐标系旋转的旋转矩阵，并以伸缩系数为对角线元素的系数。在摄象机标定中不涉及伸缩，因此该</span><span style="font-size: 12pt">3X3</span><span style="font-size: 12pt">矩阵可以更确切地用下式确定，其中&Psi;，&Theta;，&phi;为</span><span style="font-size: 12pt">XYZ</span><span style="font-size: 12pt">轴的旋转角度。</span></div><div style="text-indent: 21.75pt">&nbsp;</div><div style="text-indent: 21.75pt"><span style="font-size: 12pt">在摄象机标定中，虽说为了避免标定平面和像平面平行</span><span style="font-size: 12pt">[</span><span style="font-size: 12pt">原因下述</span><span style="font-size: 12pt">]</span><span style="font-size: 12pt">而一定要让标定板偏移一定角度（实际上只要不做精确平行对准，都能满足），但一般实际旋转角度不会太大，尤其在本实验中。标定板顶多只有稍微的倾斜，故应该满足&Psi;，&Theta;，&phi;接近</span><span style="font-size: 12pt">0</span><span style="font-size: 12pt">的水平，此时</span><span style="font-size: 12pt">R</span><span style="font-size: 12pt">趋近于一个单位阵。可以此作为求解量大致的检验。</span></div><div style="text-indent: 21.75pt"><span style="font-size: 12pt"><span style="font-size: 12pt"><a href="http://www.zwqxin.com/archives/image-processing/camera-calibration-2.html"><img style="width: 426px; height: 68px" height="65" alt="摄象机标定 from Tsai www.zwqxin.com" width="428" src="http://i3.6.cn/cvbnm/c3/7b/3c/28629b8ecea6db1a11e0fa919d1340ca.jpg" /></a></span></span></div><div style="text-indent: 21.75pt"><span style="font-size: 12pt"><span style="font-size: 12pt"><div style="text-indent: 21.75pt"><span style="font-size: 12pt">而第四列（</span><span style="font-size: 12pt">Tx</span><span style="font-size: 12pt">，</span><span style="font-size: 12pt"> Ty</span><span style="font-size: 12pt">，</span><span style="font-size: 12pt"> Tz</span><span style="font-size: 12pt">，</span><span style="font-size: 12pt">1</span><span style="font-size: 12pt">）</span><sup><span style="font-size: 12pt">T </span></sup><span style="font-size: 12pt">则是平移参量。</span></div><div style="text-indent: 21.75pt">&nbsp;</div><div style="text-indent: 21.75pt"><b><span style="font-size: 12pt">步骤</span></b><b><span style="font-size: 12pt">4</span></b><b><span style="font-size: 12pt">：确定</span></b><b><span style="font-size: 12pt">Y</span></b><b><span style="font-size: 12pt">向平移参量</span></b><b><span style="font-size: 12pt">Ty</span></b><b><span style="font-size: 12pt">的正负性</span></b></div><div style="text-indent: 21.75pt"><span style="font-size: 12pt">从</span><span style="font-size: 12pt">Ty^2</span><span style="font-size: 12pt">到</span><span style="font-size: 12pt">Ty</span><span style="font-size: 12pt">，关键在于判断</span><span style="font-size: 12pt">Ty</span><span style="font-size: 12pt">的正负性。实际上这时候数学上无法用理论得出，需要靠实际情况定义。方法就是假设为正，然后计算出</span><span style="font-size: 12pt">3X3</span><span style="font-size: 12pt">矩阵</span><span style="font-size: 12pt">R</span><span style="font-size: 12pt">的前两行数据并与（</span><span style="font-size: 12pt; color: #2a2a2a">Xw Yw Zw</span><span style="font-size: 12pt">）</span><sup><span style="font-size: 12pt">T</span></sup><span style="font-size: 12pt">乘以获得</span><span style="font-size: 12pt; color: #2a2a2a">Xw Yw Zw</span><span style="font-size: 12pt; color: #2a2a2a">在图像坐标系下的坐标（</span><span style="font-size: 12pt; color: #2a2a2a">Xd`, Yd`</span><span style="font-size: 12pt; color: #2a2a2a">）</span><sup><span style="font-size: 12pt">T</span></sup><span style="font-size: 12pt; color: #2a2a2a">，看与实际图像坐标数据（</span><span style="font-size: 12pt; color: #2a2a2a">Xd, Yd</span><span style="font-size: 12pt; color: #2a2a2a">）</span><sup><span style="font-size: 12pt">T</span></sup><span style="font-size: 12pt; color: #2a2a2a">是否同号，若不同号则假设失败，说明</span><span style="font-size: 12pt">Ty</span><span style="font-size: 12pt">应取负数，否则则为正。</span></div><div style="text-indent: 21.75pt"><span style="font-size: 12pt">怎样选取这个参考的数据点呢？为了保证不被误差影响，应用一个远离成像中心的点。</span></div><div style="text-indent: 21.75pt">&nbsp;</div><div style="text-indent: 21.75pt"><b><span style="font-size: 12pt">步骤</span></b><b><span style="font-size: 12pt">5</span></b><b><span style="font-size: 12pt">：计算视图变换矩阵参数</span></b><b><span style="font-size: 12pt">(r1,...,r9,Tx,Ty)</span></b></div><div style="text-indent: 21.75pt"><span style="font-size: 12pt">有了精确正负的</span><span style="font-size: 12pt">Ty</span><span style="font-size: 12pt">，结合步骤</span><span style="font-size: 12pt">2</span><span style="font-size: 12pt">得出的</span><span style="font-size: 12pt"> Ty^(-1)*r1, Ty^(-1)*r2, Ty^(-1)*Tx, Ty^(-1)*r4, Ty^(-1)*r5</span><span style="font-size: 12pt">，就可以得到精确正负的</span><span style="font-size: 12pt">r1, r2, r4, r5,Tx</span><span style="font-size: 12pt">了。观察上面的</span><span style="font-size: 12pt">R</span><span style="font-size: 12pt">矩阵，满足</span><span style="font-size: 12pt">X</span><span style="font-size: 12pt">，</span><span style="font-size: 12pt">Y</span><span style="font-size: 12pt">，</span><span style="font-size: 12pt">Z</span><span style="font-size: 12pt">方向旋转向量的单位性，以及各旋转向量的正交性。因此</span><span style="font-size: 12pt">r3, r6</span><span style="font-size: 12pt">可由此得出：</span></div><div style="text-indent: 21.75pt; text-align: center"><span style="font-size: 12pt"><span style="font-size: 12pt"><a href="http://www.zwqxin.com/archives/image-processing/camera-calibration-2.html"><img alt="摄象机标定 from Tsai www.zwqxin.com" src="http://lh5.ggpht.com/_lYWT-2PnV0s/Sro9tn4cPEI/AAAAAAAAA8A/HFBSFTJKrM8/s800/Snap3090715.jpg" /></a></span></span></div><div style="text-indent: 21.75pt; text-align: center">&nbsp;</div><div style="text-indent: 21.75pt"><span style="font-size: 12pt">r7,r8,r9</span><span style="font-size: 12pt">的大小可由上两行向量的叉积得出：</span></div><div style="text-indent: 21.75pt" align="center"><span style="font-size: 12pt">r7 = r0 * r6 - r3 * r5;</span></div><div style="text-indent: 21.75pt" align="center"><span style="font-size: 12pt">r8 = r3 * r4 &ndash;r1 * r6;</span></div><div style="text-indent: 21.75pt" align="center"><span style="font-size: 12pt">r9 = r1 * r5 &ndash;r2 * r4;</span></div><div style="text-indent: 21.75pt"><span style="font-size: 12pt">至于</span><span style="font-size: 12pt">r7,r8,r9</span><span style="font-size: 12pt">的方向性同样可通过与上述类似的</span><span style="font-size: 12pt">假设判断</span><span style="font-size: 12pt"> &ndash;&gt;</span><span style="font-size: 12pt">验证假设</span><span style="font-size: 12pt">的方式判断，这就需要先知道怎么求解最终结果。但正因为只需要求取其方向性，因此只需要近似的数值解就可以了。</span></div><div style="text-indent: 21.75pt">&nbsp;</div><div style="text-indent: 21.75pt"><b><span style="font-size: 12pt">步骤</span></b><b><span style="font-size: 12pt">6</span></b><b><span style="font-size: 12pt">：计算视图变换矩阵参数</span></b><b><span style="font-size: 12pt">Tz</span></b><b><span style="font-size: 12pt">，以及焦距</span></b><b><span style="font-size: 12pt">f</span></b></div></span></span></div><p style="text-align: center"><span style="font-size: 12pt"><span style="font-size: 12pt"><a href="http://www.zwqxin.com/archives/image-processing/camera-calibration-2.html"><img alt="摄象机标定 from Tsai www.zwqxin.com" src="http://lh4.ggpht.com/_lYWT-2PnV0s/Sro9t9_-gMI/AAAAAAAAA8E/hT6jpbk9VSU/s800/Snap3090716.jpg" /></a></span></span></p><p><p>&nbsp;</p></p><div style="text-indent: 21.75pt"><span style="font-size: 12pt">以上的结果，</span><span style="font-size: 12pt">f</span><span style="font-size: 12pt">作为近似值，其正负作为步骤</span><span style="font-size: 12pt">5</span><span style="font-size: 12pt">中</span><span style="font-size: 12pt">r7,r8,r9</span><span style="font-size: 12pt">的方向性的判断依据。</span></div><div style="text-indent: 21.75pt"><span style="font-size: 12pt">根据</span><span style="font-size: 12pt">TSAI</span><span style="font-size: 12pt">的论文，本方程若作为超定方程求解，则需要标定平面和像平面平行的约束条件。求出的</span><span style="font-size: 12pt">Tz</span><span style="font-size: 12pt">，</span><span style="font-size: 12pt"> f</span><span style="font-size: 12pt">，连同</span><span style="font-size: 12pt">s = 0 </span><span style="font-size: 12pt">作为迭代初始值</span><span style="font-size: 12pt">, </span><span style="font-size: 12pt">进行模型最终阶段的非线性求解。</span></div><div style="text-indent: 21.75pt"><span style="font-size: 12pt">求超定方程的最小二乘解，常用的方法有：<b>对称矩阵的三角分解法</b>和<b>矩阵的</b></span><b><span style="font-size: 12pt">QR</span></b><b><span style="font-size: 12pt">分解法</span></b><span style="font-size: 12pt">。本程序采用前者。</span><span style="font-size: 12pt">[3][4]</span></div><div style="text-indent: 21.75pt"><span style="font-size: 12pt">考虑超定方程：</span><span style="font-size: 12pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><i>GX=b</i></div><div style="text-indent: 21.75pt"><span style="font-size: 12pt">方程左右同乘以系数矩阵</span><span style="font-size: 12pt">G</span><span style="font-size: 12pt">的转置</span><i>G<sup>T</sup></i><span style="font-size: 12pt">，得：</span></div><div style="text-indent: 158.25pt"><i>G<sup>T</sup>GX=G<sup>T</sup>b</i></div><div style="text-indent: 21.75pt">因此方程转化成<span style="font-size: 12pt">正规方程组，</span>G<sup>T</sup>G的列数与X的行数相同，且该数与方程中未知数数量相等。通过常规方法可解此方程：</div><p><i><span style="font-size: 12pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; X=</span></i><span style="font-size: 12pt">(<i>G<sup>T</sup>G</i>)<sup>-1</sup><i>G<sup>T</sup>b</i></span></p><p><span style="font-size: 12pt"><em>ＬＶideoCapture .ver1.8 结果(标定的是.ver1.5中的圆心坐标</em>[<a target="_blank" href="http://www.zwqxin.com/archives/image-processing/extract-center-of-circle.html">圆心坐标的提取</a>] <em>):</em></span></p><p style="text-align: center"><span style="font-size: 12pt"><i><span style="font-size: 12pt"><span style="font-size: 12pt"><a href="http://www.zwqxin.com/archives/image-processing/camera-calibration-2.html"><img alt="摄象机标定 from Tsai www.zwqxin.com" src="http://lh4.ggpht.com/_lYWT-2PnV0s/SrpFWXPIw5I/AAAAAAAAA94/3P6ltgPFgac/s800/Snap30907145.jpg" /></a></span></span></i></span></p><p style="text-align: center"><span style="font-size: 12pt"><i><span style="font-size: 12pt"><span style="font-size: 12pt"><span style="font-size: 12pt"><i><span style="font-size: 12pt"><span style="font-size: 12pt"><a href="http://www.zwqxin.com/archives/image-processing/camera-calibration-2.html"><img alt="摄象机标定 from Tsai www.zwqxin.com" src="http://lh6.ggpht.com/_lYWT-2PnV0s/SrpFWx_sbtI/AAAAAAAAA98/zy0dxqzlVBA/s800/Snap30907146.jpg" /></a></span></span></i></span></span></span></i></span></p>]]></description><category>图像处理</category><comments>http://www.zwqxin.com/archives/image-processing/camera-calibration-2.html#comment</comments><wfw:comment>http://www.zwqxin.com/</wfw:comment><wfw:commentRss>http://www.zwqxin.com/feed.asp?cmt=77</wfw:commentRss><trackback:ping>http://www.zwqxin.com/cmd.asp?act=tb&amp;id=77&amp;key=44293a0a</trackback:ping></item><item><title>摄像机标定技术 PART1</title><author>a@b.com (zwqxin)</author><link>http://www.zwqxin.com/archives/image-processing/camera-calibration-1.html</link><pubDate>Thu, 09 Jul 2009 20:42:31 +0800</pubDate><guid>http://www.zwqxin.com/archives/image-processing/camera-calibration-1.html</guid><description><![CDATA[<p>&nbsp;老实说，我对计算机视觉的兴趣没什么，特别是用于工程计算的这类。但是，毕竟自己也花费了不少的时间精力在这个东西上面（成效没什么），也便于骗骗稿费（自己给自己的~）&mdash;&mdash;<a href="http://www.zwqxin.com/">ZwqXin</a>.com</p><p>首先说的是，摄象机标定这个东西是计算机视觉里面的基础，然后又有什么蔡的法啊张的法啊的。这里我接触的就是蔡的方法，其实这个蔡(Tsai)啊，一点都不菜，居然想出这样的东西出来&hellip;&hellip;其大作是：<strong><span lang="EN-US" style="font-size: 9pt; font-family: 宋体; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><font face="Arial">TSAI </font></span></strong><span lang="EN-US" style="font-size: 9pt; font-family: 宋体; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><font face="Arial">- A Versatile Camera Calibration Technique for High-Accuracy 3D Machine Vision Metrology Using Off-the-shelf TV Cameras and Lenses。</font></span>是的，它这论文名字也够气势，居然那么长 - -。论文的话各位可能药费力找找了，我自己也找不着，还是人家有的发给我看的，也不乱传播了。</p><p><span style="color: #ff0000"><strong><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">摄像机标定技术</span></strong></span></p><p><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">三维重建是人类视觉的主要目的，也是计算机视觉的最主要的研究方向. (Marr 1982)。</span></p><p><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">所谓三维重建就是指从图像出发恢复出空间点三维坐标的过程。包括以下三步骤：<br />1.图像对应点的确定<br />&nbsp; 2.摄像机标定<br />&nbsp; 3.二图像间摄像机运动参数的确定</span></p><p><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">空间物体表面某点的三维几何位置与其在图像中对应点之间的相互关系是由摄像机成像的几何模型决定的，这些几何模型参数就是摄像机参数。在大多数条件下，这些参数必须通过实验与计算才能得到，这个过程被称为摄像机定标(或称为标定)。标定精度的大小，直接影响着计算机视觉（机器视觉）的精度。</span></p><p><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">根据是否需要标定参照物来看，可分为传统的摄像机标定方法和摄像机自标定方法。[前者用于摄像机的参数不经常变化时，后者用于精度要求不高的场合，如通讯、虚拟现实等。]从所用模型不同来分，线性和非线性。</span></p><p><span style="color: #ff0000"><strong><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">摄像机标定模型</span></span></strong></span></p><p>所谓摄像机的线性模型，是指经典的小孔模型。而非线性模型摄像机标定，考虑了畸变参数，引入了非线性优化，但方法较繁，速度慢，对初值选择和噪声比较敏感，而且非线性搜索并不能保证参数收敛到全局最优解。这里用线性模型。</p><p style="text-align: center">&nbsp;<a href="http://www.zwqxin.com/archives/image-processing/camera-calibration-1.html"><img alt="摄象机标定 from Tsai www.zwqxin.com" src="http://lh5.ggpht.com/_lYWT-2PnV0s/SronEyfn7YI/AAAAAAAAA68/3eDa_pA-YjE/s800/Snap109708.jpg" /></a></p><p>参见上图（截取自RAC算法提出者ROGER.Y.TSAI的论文），P(xw, yw, zw)是实际空间中的坐标定，定位于&ldquo;世界坐标系&rdquo;，其圆心和方向可由任何人制定（在算法中关注的仅仅是数据点的相对位置）。</p><p>而在摄像机的观察世界中，必然存在着以摄像机为自我的本位坐标系，我们称为摄象机坐标系（在计算机图形学中习惯称为视图坐标系），它以镜头中心为坐标原点，Z轴垂直镜头平面向被观察物的方向延伸。</p><p style="text-align: center"><a href="http://www.zwqxin.com/archives/image-processing/camera-calibration-1.html"><img alt="摄象机标定 from Tsai www.zwqxin.com" src="http://lh5.ggpht.com/_lYWT-2PnV0s/SronFO8GvUI/AAAAAAAAA7A/f20TTasYHDw/s800/Snap209708.jpg" /></a><br />(<span style="font-size: 9.5pt; color: #2a2a2a; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">图</span><span lang="EN-US" style="font-size: 9.5pt; color: #2a2a2a; font-family: &quot;Times New Roman&quot;; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体">2 </span><span style="font-size: 9.5pt; color: #2a2a2a; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">坐标变换</span>)</p><p>空间的一点或点集，从世界坐标系向摄象机坐标系的转换，在计算机图形学中习惯称为&ldquo;视图变换&rdquo;。本身点或点集不改变，改变的仅仅是参照系，或者说，描述角度。这通常通过设定相机的观察矩阵来设置&mdash;&mdash;实质上就是仿射变换（不涉及伸缩变换，只包括平移和旋转）。在假想情景中，可以看作移动点或点集至两空间各面平行为止（实际是移动坐标系）。 假如设定世界坐标系原点为标定板角点，XY轴沿板边向，那么摄像机标定所用仿射矩阵，将与空间运动学中把标定板按实际旋转、移动到平行于镜头平面时所用的矩阵一致。</p><p>摄象机按照小孔成像原理进行投影变换（实质是三角形相似定理获取的透视图像）到达摄象机内部的成像平面而形成图像。在计算机中呈现的是像素尺寸拉大后的原始图像。投影变换后仍然以成像平面中心（图像中心像素位置）为原点[如果忽略畸变的话]。进行尺寸和原点位置的简单变换后可得图像空间描述的原始点/点集位置，此时原点在图像左上角点。</p><p style="text-align: center">&nbsp;<a href="http://www.zwqxin.com/archives/image-processing/camera-calibration-1.html"><img alt="摄象机标定 from Tsai www.zwqxin.com" src="http://lh4.ggpht.com/_lYWT-2PnV0s/SronFuJrgwI/AAAAAAAAA7E/vOe--_fJZ40/s512/Snap3090708.jpg" /></a><br />(<span style="font-size: 9.5pt; color: #2a2a2a; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">图3</span><span lang="EN-US" style="font-size: 9.5pt; color: #2a2a2a; font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"> </span><span style="font-size: 9.5pt; color: #2a2a2a; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 9.5pt; color: #2a2a2a; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">坐标变换序列图</span></span>)</p><p>&nbsp;<span lang="EN-US" style="font-size: 10.5pt; color: #2a2a2a; font-family: &quot;Times New Roman&quot;; mso-bidi-font-size: 12.0pt; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体">TSAI</span><span style="font-size: 10.5pt; color: #2a2a2a; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">的</span><span lang="EN-US" style="font-size: 10.5pt; color: #2a2a2a; font-family: &quot;Times New Roman&quot;; mso-bidi-font-size: 12.0pt; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体">RAC</span><span style="font-size: 10.5pt; color: #2a2a2a; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">模型是运用得比较广泛的。其核心是求出矩阵变换中的各个参量的准确数值解，为今后的测量提供多方面的准备。</span></p><p style="text-align: center"><span style="font-size: 10.5pt; color: #2a2a2a; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><a href="http://www.zwqxin.com/archives/image-processing/camera-calibration-1.html"><img alt="摄象机标定 from Tsai www.zwqxin.com" src="http://lh6.ggpht.com/_lYWT-2PnV0s/SroqIcuCAJI/AAAAAAAAA7g/jw4wCsCYrjs/s800/Snap3090709.jpg" /></a></span></p><p style="text-align: center"><span style="font-size: 10.5pt; color: #2a2a2a; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><font color="#000000">(</font></span><span style="font-size: 10.5pt; color: #2a2a2a; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; color: #2a2a2a; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">正交旋转矩阵实际上只含有</span><span lang="EN-US" style="font-size: 10.5pt; color: #2a2a2a; font-family: &quot;Times New Roman&quot;; mso-bidi-font-size: 12.0pt; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体">3 </span><span style="font-size: 10.5pt; color: #2a2a2a; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">个独立变量，再加上</span><span lang="EN-US" style="font-size: 10.5pt; color: #2a2a2a; font-family: &quot;Times New Roman&quot;; mso-bidi-font-size: 12.0pt; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体">x y z t ,t ,</span><span style="font-size: 10.5pt; color: #2a2a2a; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">和</span><span lang="EN-US" style="font-size: 10.5pt; color: #2a2a2a; font-family: &quot;Times New Roman&quot;; mso-bidi-font-size: 12.0pt; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体">t </span><span style="font-size: 10.5pt; color: #2a2a2a; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">，总工有</span><span lang="EN-US" style="font-size: 10.5pt; color: #2a2a2a; font-family: &quot;Times New Roman&quot;; mso-bidi-font-size: 12.0pt; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体">6 </span><span style="font-size: 10.5pt; color: #2a2a2a; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">个参数决定了摄像机光轴在世界坐标系中空间位置，因此这六个参数称为摄像机外部参数。</span><span lang="EN-US" style="font-size: 10.5pt; color: #2a2a2a; font-family: &quot;Times New Roman&quot;; mso-bidi-font-size: 12.0pt; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体">f,s,u0,v0</span><span style="font-size: 10.5pt; color: #2a2a2a; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">四个参数与摄象机内部结构有关因此称内部参数。其中</span><span lang="EN-US" style="font-size: 10.5pt; color: #2a2a2a; font-family: &quot;Times New Roman&quot;; mso-bidi-font-size: 12.0pt; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体">f</span><span style="font-size: 10.5pt; color: #2a2a2a; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">是计算机焦距，</span><span lang="EN-US" style="font-size: 10.5pt; color: #2a2a2a; font-family: &quot;Times New Roman&quot;; mso-bidi-font-size: 12.0pt; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体">s</span><span style="font-size: 10.5pt; color: #2a2a2a; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">是畸变参数，</span><span lang="EN-US" style="font-size: 10.5pt; color: #2a2a2a; font-family: &quot;Times New Roman&quot;; mso-bidi-font-size: 12.0pt; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体">u0,v0</span><span style="font-size: 10.5pt; color: #2a2a2a; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">是图像中心像素坐标。</span></span></p><p><span style="font-size: 10.5pt; color: #2a2a2a; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; color: #2a2a2a; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; color: #2a2a2a; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">与</span><span lang="EN-US" style="font-size: 10.5pt; color: #2a2a2a; font-family: &quot;Times New Roman&quot;; mso-bidi-font-size: 12.0pt; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体">DLT法（直接对物理-图像转换式子配超定方程）</span><span style="font-size: 10.5pt; color: #2a2a2a; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">相比，优点在于参量的可重用性，不依赖于特定的部分变量，求解的精确性快速性。不过需要获知准确的摄象机参数。</span></span></span></p><p><span style="font-size: 10.5pt; color: #2a2a2a; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; color: #2a2a2a; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; color: #2a2a2a; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">（以上多为本人粗略见解,未必准确,请看官疑虑）</span></span></span></p><p><span style="font-size: 10.5pt; color: #2a2a2a; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; color: #2a2a2a; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; color: #2a2a2a; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 9.5pt; color: #2a2a2a; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 9.5pt; color: #2a2a2a; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">坐标变换的过程见下篇：<a target="_self" href="http://www.zwqxin.com/archives/image-processing/camera-calibration-2.html">摄像机标定技术 PART2</a></span></span></span></span></span></p>]]></description><category>图像处理</category><comments>http://www.zwqxin.com/archives/image-processing/camera-calibration-1.html#comment</comments><wfw:comment>http://www.zwqxin.com/</wfw:comment><wfw:commentRss>http://www.zwqxin.com/feed.asp?cmt=76</wfw:commentRss><trackback:ping>http://www.zwqxin.com/cmd.asp?act=tb&amp;id=76&amp;key=a55f8fed</trackback:ping></item><item><title>圆心坐标的提取</title><author>a@b.com (zwqxin)</author><link>http://www.zwqxin.com/archives/image-processing/extract-center-of-circle.html</link><pubDate>Sun, 28 Jun 2009 19:37:37 +0800</pubDate><guid>http://www.zwqxin.com/archives/image-processing/extract-center-of-circle.html</guid><description><![CDATA[<p>&nbsp;用OpenCV的话，这样的活儿也就那么两三行代码，而且灵活可靠。但是，玩图像处理只懂玩成这样就悲哀了，就如同游戏引擎之于计算机图形学，太依赖图像库是不利于自己的成长和知识的提升的~&mdash;&mdash;<a href="http://www.zwqxin.com/">ZwqXin</a>.com</p><p>圆心提取的本质是从一张含有圆形/椭圆标志物的图片（见下图1，现实图片经各种处理到达这种圆与背景分明的样态）中，选择一定坐标系，提取出各个标志圆的圆心在这个坐标系中的坐标。</p><p>因此这个坐标系的选择将是影响最终结果的形式的关键。一般来说选择图像坐标系，以像素为单位。本实验图片的格式是BMP，因此可以按照BMP自身的坐标特点，<strong><span style="color: #0000ff">以图片左下角为坐标原点，向右为X轴，向上为Y轴</span></strong>。当然若想与WINDOWS窗口坐标系一致（以左上角为原点），直接用图片高度减上坐标系的结果就行了。</p><p>关于结果的精度是很值得考究的事情。事实上，若考虑到素材图片是相机所拍成像的，某个圆的圆心与坐标原点的距离应该是一个含一定精度的浮点实数。但是计算机图像的结构限定了坐标系只能以像素为单位，因此其<strong>可能达到的最高精度只有0. 5像素</strong>。在此以上的精度在本素材图像中是没有意义的。因为相机会自动对物理场景（连续）的采样点（离散采样）进行向输出图像的一一映射，形成像素（一个像素只包含一款颜色）。相邻像素的值是非连续（离散）的，注定图像坐标系中每个&ldquo;值&rdquo;（坐标）都只能与像素相容。这样，圆心位置也就只能存在于某个像素上或某2个或4个像素之间，因此实际中最高只有0.5的精度。</p><p style="text-align: center">&nbsp;<a target="_self" href="http://www.zwqxin.com/archives/image-processing/extract-center-of-circle.html"><img alt="提取圆心坐标 www.zwqxin.com" src="http://lh4.ggpht.com/_lYWT-2PnV0s/SrZsJQH4PTI/AAAAAAAAA5o/zk65mUy5SSQ/s800/Snap623.jpg" /></a><br /><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">（图</span><span lang="EN-US" style="font-size: 10.5pt; font-family: 'Times New Roman'; mso-bidi-font-size: 12.0pt; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体">1&nbsp; </span><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">圆心提取素材图片）</span><span lang="EN-US" style="font-size: 10.5pt; font-family: 'Times New Roman'; mso-bidi-font-size: 12.0pt; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><br style="mso-special-character: line-break" /></span><a target="_self" href="http://www.zwqxin.com/archives/image-processing/extract-center-of-circle.html"><img alt="提取圆心坐标 www.zwqxin.com" src="http://lh4.ggpht.com/_lYWT-2PnV0s/SrZsJiFDYhI/AAAAAAAAA5s/4Lzhx2AX1XU/s800/Snap623-1.jpg" /></a><br /><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">（图</span><span lang="EN-US" style="font-size: 10.5pt; font-family: 'Times New Roman'; mso-bidi-font-size: 12.0pt; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体">2 </span><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">数字图像本质&mdash;&mdash;被离散化采样，导致精度最高仅</span><span lang="EN-US" style="font-size: 10.5pt; font-family: 'Times New Roman'; mso-bidi-font-size: 12.0pt; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体">0.5</span><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">）</span></p><p><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">但是，如果圆心不是通过直接寻找的，而是采用数值计算得到（譬如本实验中用到的径向误差判断结合法），那么计算结果就会存在比较高的精度的可能性。这是理论可得结果与实际可得结果的差别。但就&ldquo;计算机图像&rdquo;这层意义来说，高于0.5的精度意义不大。</span></p><p><span style="color: #ff0000"><strong><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">算法的设计：</span></span></strong></span></p><p><span style="color: #000000"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">在分析之后对算法的设计和选择就清晰了&mdash;&mdash;立足于图像的本质上的话，只需要把圆心所在的像素位置找到，或者把包围圆心位置的几个像素找到即可。</span></span></span></p><p><span style="color: #000000"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">参见图2，可知，图像中的参照物为一个圆或椭圆。在把它看作理想的圆/椭圆的话，可以认为圆点必然是在X方向横跨最多像素点的那一行中。因此算法设计上，针对每个圆，首先找出满足这个条件的一行（或几行，取中间行或中间两行），然后在Y方向上找到该像素行的中间位置，即为圆心&mdash;&mdash;综合来说，圆心位置可能是落在某一个像素上，或X方向两个像素之间，或Y方向两个像素之间，或XY方向的4个像素之间。算法必须考虑这几种情况，算出精度为0.5的圆心位置。</span></span></span></p><p><span style="color: #000000"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">算法的实现重点在于怎样分离出一个一个的圆，我采用的是逐像素法，先处理完最开头的一个圆（底部坐标靠近左下角原点），然后把这个圆从图像中去除（对二值化图像，就是把该圆的白色像素变黑），之后再处理下一个圆（寻得条件同样是，底部坐标靠近左下角原点）&hellip;&hellip;直到处理完。怎么判断处理完呢？具体就是当该图像完全黑化的时候停止继续寻觅圆。</span></span></span></p><p><span style="color: #000000"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">圆心坐标用一个容器链表保存起来，作为输出&mdash;&mdash;输出到文件或作为一个/几个白像素涂入被完全黑化的图像上。</span></span></span></p><p><span style="color: #000000"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">该算法为自主设计，自称为逐像素处理。应用条件是：<br />（1）&nbsp;图片为二值化黑白图片，8BIT。（可以通过简单的预处理达到）<br />（2）&nbsp;图片中仅含圆形。（本实验素材满足，若不满足可进行一定的预处理，如形态学处理，空间模板处理）</span></span></span></p><p><span style="color: #000000"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">上方法另外作出的一个假设：该圆为理想的圆/椭圆。但是从图2这个放大图来说，事实上并不理想&mdash;&mdash;虽然也可满足以上算法能找到圆心。把该圆规范化成一个标准的圆，这样可使找出的圆心具有更高的可信度。</span></span></span></p><p><span style="color: #000000"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">按照某文献资料(<span style="font-size: 9pt; font-family: 宋体; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><strong>图像处理中圆心算法研究</strong></span><span lang="EN-US" style="font-size: 9pt; font-family: 宋体; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"> - </span><span style="font-size: 9.5pt; color: #2a2a2a; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">雷家勇</span><span lang="EN-US" style="font-size: 9.5pt; color: #2a2a2a; font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体">,</span><span style="font-size: 9.5pt; color: #2a2a2a; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">达飞鹏</span><span lang="EN-US" style="font-size: 9.5pt; color: #2a2a2a; font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体">,</span><span style="font-size: 9.5pt; color: #2a2a2a; font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">孟广猛，东南大学自动化研究所)</span>中所叙述的方法，可采用&ldquo;径向误差预处理的最小二乘&rdquo;进行拟合计算。</span></span></span></p><p><span style="color: #000000"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">算法可分为三部分：<br />1.&nbsp;找出初始圆心（此圆心作为径向误差判断的基准，在本实验中可直接取用逐像素处理法所得的结果）。<br />2.&nbsp;找出拟合用的像素坐标。首先这些像素必须是圆形的边缘单像素（即八邻域内最多3个像素）；然后进行筛选，这主要通过径向误差范围判断，去除无效的边界点和误差大的边界点。<br />3.&nbsp;根据上述处理后的边界点，用最小二乘法拟合圆心。</span></span></span></p><p><span style="color: #000000"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">因为经历了数值计算，因此结果将具有高精度，但不一定有意义（见4.1）。本方法结合了&ldquo;逐像素法找初始圆心&rdquo;&mdash;&mdash;&ldquo;径向误差筛选出拟合数据&rdquo;&mdash;&mdash;&ldquo;最小二乘法拟合、修正圆心&rdquo;，得到可信度更高的圆心方位。</span></span></span></p><p><span style="color: #000000"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">该算法（径向误差最小二乘法）的适用条件为：<br />（1）&nbsp;处理的图像元素必须是严格的圆或近似圆。如果是椭圆且偏心太厉害的话，全部基于圆的拟合算法会不适用，径向误差方法也变得无效。<br />（2）&nbsp;因为采用了逐像素处理，因此也必须满足之前所述该算法的条件。</span></span></span></p><p><span style="color: #000000"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">有此设计的处理结果是:</span></span></span></p><p style="text-align: center"><span style="color: #000000"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><a target="_self" href="http://www.zwqxin.com/archives/image-processing/extract-center-of-circle.html"><img alt="提取圆心坐标 www.zwqxin.com" src="http://lh3.ggpht.com/_lYWT-2PnV0s/SrZsKJwcm9I/AAAAAAAAA5w/cTDbq0CD4OE/s800/Snap623-2.jpg" /></a><br /><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">（</span><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">图</span><span lang="EN-US" style="font-size: 10.5pt; font-family: 'Times New Roman'; mso-bidi-font-size: 12.0pt; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体">3&nbsp; </span><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">程序界面，圆心提取对话框）</span></span><span lang="EN-US" style="font-size: 10.5pt; font-family: 'Times New Roman'; mso-bidi-font-size: 12.0pt; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体"><br style="mso-special-character: line-break" /></span><a target="_self" href="http://www.zwqxin.com/archives/image-processing/extract-center-of-circle.html"><img alt="提取圆心坐标 www.zwqxin.com" src="http://lh4.ggpht.com/_lYWT-2PnV0s/SrZsKnWnhVI/AAAAAAAAA50/f390p1iLDuw/s800/Snap623-3.jpg" /></a><br /><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">（图</span><span lang="EN-US" style="font-size: 10.5pt; font-family: 'Times New Roman'; mso-bidi-font-size: 12.0pt; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体">4&nbsp;&nbsp;结果</span><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">）<br /><span style="color: #000000"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><a target="_self" href="http://www.zwqxin.com/archives/image-processing/extract-center-of-circle.html"><img alt="提取圆心坐标 www.zwqxin.com" src="http://lh4.ggpht.com/_lYWT-2PnV0s/SrZsK1zWY1I/AAAAAAAAA54/i5sBGI9zmIc/s800/Snap623-4.jpg" /></a><br />(<span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">图</span><span lang="EN-US" style="font-size: 10.5pt; font-family: 'Times New Roman'; mso-bidi-font-size: 12.0pt; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体">5&nbsp; 直观一点</span></span>)</span></span></span></span></span></span></span></p><p style="text-align: center"><span style="color: #000000"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><a target="_self" href="http://www.zwqxin.com/archives/image-processing/extract-center-of-circle.html"><img alt="提取圆心坐标 www.zwqxin.com" src="http://lh5.ggpht.com/_lYWT-2PnV0s/SrZsLB5qlnI/AAAAAAAAA58/UZKofQyUgiE/s800/Snap623-5.jpg" /></a><br /><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">（图6</span><span lang="EN-US" style="font-size: 10.5pt; font-family: 'Times New Roman'; mso-bidi-font-size: 12.0pt; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体">&nbsp;&nbsp;另一张测试图片</span><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">）<br /><span style="color: #000000"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><a target="_self" href="http://www.zwqxin.com/archives/image-processing/extract-center-of-circle.html"><img alt="提取圆心坐标 www.zwqxin.com" src="http://lh4.ggpht.com/_lYWT-2PnV0s/SrZsLjR3P2I/AAAAAAAAA6E/CZ3EJWylHt4/s800/Snap623-6.jpg" /></a><br /><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">（图7</span><span lang="EN-US" style="font-size: 10.5pt; font-family: 'Times New Roman'; mso-bidi-font-size: 12.0pt; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体">&nbsp; 处理结果</span><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">）</span></span></span></span></span></span></span></span></p><p><span style="color: #000000"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">最后这里只是说明对于这种杂乱排布的圆也能检测出而已。不过扫描算法里的递归终结条件就得好好掂量了~</span></span></span></span></p><p><span style="color: #000000"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">程序内嵌进LVideoCapture .ver 1.5里了。（1.0版的功能见[<a target="_blank" href="http://www.zwqxin.com/archives/image-processing/vfw-capture-try.html">VFW视频捕获之尝试</a>] ）在后面的1.8版本，涉及了摄象机标定的功能。</span></span></span></span></p><p><span style="color: #000000"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><a href="http://code.google.com/p/lvideocapture/downloads/list">http://code.google.com/p/lvideocapture/downloads/list</a>&nbsp; </span></span></span></span><br /><span style="color: #000000"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">LVideoCapture Demo .ver1.5.rar</span></span></span></span></p>]]></description><category>图像处理</category><comments>http://www.zwqxin.com/archives/image-processing/extract-center-of-circle.html#comment</comments><wfw:comment>http://www.zwqxin.com/</wfw:comment><wfw:commentRss>http://www.zwqxin.com/feed.asp?cmt=75</wfw:commentRss><trackback:ping>http://www.zwqxin.com/cmd.asp?act=tb&amp;id=75&amp;key=3d391cbc</trackback:ping></item><item><title>图像色彩空间与HSL/HSV</title><author>a@b.com (zwqxin)</author><link>http://www.zwqxin.com/archives/image-processing/image-hsl-hsv.html</link><pubDate>Wed, 17 Jun 2009 23:39:24 +0800</pubDate><guid>http://www.zwqxin.com/archives/image-processing/image-hsl-hsv.html</guid><description><![CDATA[<p>&nbsp;我们用RGBA来描述一个像素的颜色，没错，这对计算机来说最方便不过？不过除了RGB，你还听说过HSL/HSV吗？&mdash;&mdash;<a href="http://www.zwqxin.com/">ZwqXin</a>.com</p><p>其实不只HSL/HSV，还有什么CMYK啊，LAB啊的，都是采用不同的标准去描述颜色。人所见到的颜色是有限界的，因此真实的颜色永远不会被穷举完成；同时，人所能感受的颜色差别更是有限，因此就有了有限的表示法。</p><p style="text-align: center"><img alt="" src="http://www.xiangji.cn/school/2006/20061221103031491.jpg" /><br /><br/>（<strong>CIE 1931 色彩空间</strong>，最先采用数学方式来定义的色彩空间，基于人类颜色视觉的直接测定。由国际照明委员会（CIE）于1931年创立）</p><p>sRGB色彩空间是美国的惠普公司和微软公司于1997年共同开发的标准色彩空间（standard Red Green Blue），AdobeRGB色彩空间是由美国以开发Photoshop软件而闻名的Adobe公司1998年推出的色彩空间标准，但是它们都远远没能包含整个CIE 1931色彩空间（基于人类颜色视觉的直接测定，包含人类所感知的最丰富色表），可见计算机在颜色表现这块上的局限性。</p><p>色彩空间，是一种对颜色的描述方法，依照其描述方法而形成一个装载颜色的容器。譬如最常用的RGB法，把颜色分到红绿蓝三通道，是一种非线性的描述方式；LAB中，<strong><em>L</em></strong> 表示亮度，<i><b>a</b></i> 和 <i><b>b</b></i> 表示颜色对立维度，是一种感知线性的描述方式。前者（以及HSL/HSV等）依赖于具体设备，后者（以及CMYK等）则独立于设备，因此前者和后者不可以简单地相互转换，需要通过中间媒介（sRGB或AdobeRGB这种一定独立设备的设备内描述方式）进行转换。</p><p>而这次实现的RGB到HSL/HSV空间的转换，和HSL/HSV到RGB的转换，则没有那么麻烦，它们都是设备相关的非线性颜色描述方式。HSL表示 <b>h</b>ue（色相）、<b>s</b>aturation（饱和度）、<b>l</b>ightness（亮度），HSV 表示 <b>h</b>ue（色相）、 <b>s</b>aturation（饱和度）、<b>v</b>alue （值）。它们俩是很类似的，但又有定义上的不同，其中，前者的饱和度和后者的饱和度不是同一个定义恩。</p><p>相关的转换公式可以参考[<a target="_blank" href="http://zh.wikipedia.org/wiki/HSL%E5%92%8CHSV%E8%89%B2%E5%BD%A9%E7%A9%BA%E9%97%B4">HSL和HSV色彩空间 - 维基百科</a>]，这里以HSL和RGB互转为例，转化为代码：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">void</span><span>&nbsp;FormatHSLConvert::RGBToHSLConvert(FilterColor&nbsp;oColor,&nbsp;FilterColor&amp;&nbsp;rColor) </span></span></li>    <li><span>{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UCHAR&nbsp;MaxCol&nbsp;=&nbsp;oColor.red; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UCHAR&nbsp;MinCol&nbsp;=&nbsp;oColor.red;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(oColor.green&nbsp;&gt;&nbsp;MaxCol) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MaxCol&nbsp;=&nbsp;oColor.green; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MinCol&nbsp;=&nbsp;oColor.green; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(oColor.blue&nbsp;&gt;&nbsp;MaxCol) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MaxCol&nbsp;=&nbsp;oColor.blue; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(oColor.blue&nbsp;&lt;&nbsp;MinCol) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MinCol&nbsp;=&nbsp;oColor.blue; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">double</span><span>&nbsp;Hue&nbsp;=&nbsp;0.0;&nbsp;</span><span class="comment">//色相&nbsp;(0-360) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">double</span><span>&nbsp;saturation&nbsp;=&nbsp;0.0;&nbsp;</span><span class="comment">//饱和度&nbsp;(0，1) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">double</span><span>&nbsp;lightness&nbsp;=&nbsp;0.0;&nbsp;&nbsp;</span><span class="comment">//亮度&nbsp;(0,1) </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(CurrentHSLChannel&nbsp;==&nbsp;HCHannelHSL&nbsp;||&nbsp;CurrentHSLChannel&nbsp;==&nbsp;HSLCHannelHSL) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(MaxCol&nbsp;==&nbsp;MinCol) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Hue&nbsp;=&nbsp;0; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(MaxCol&nbsp;==&nbsp;oColor.red&nbsp;&amp;&amp;&nbsp;oColor.green&nbsp;&gt;=&nbsp;oColor.blue) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Hue&nbsp;=&nbsp;60.0&nbsp;*&nbsp;</span><span class="keyword">double</span><span>(oColor.green&nbsp;-&nbsp;oColor.blue)&nbsp;/&nbsp;(MaxCol&nbsp;-&nbsp;MinCol); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(MaxCol&nbsp;==&nbsp;oColor.red&nbsp;&amp;&amp;&nbsp;oColor.green&nbsp;&lt;&nbsp;oColor.blue) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Hue&nbsp;=&nbsp;60.0&nbsp;*&nbsp;</span><span class="keyword">double</span><span>(oColor.green&nbsp;-&nbsp;oColor.blue)&nbsp;/&nbsp;(MaxCol&nbsp;-&nbsp;MinCol)&nbsp;+&nbsp;360; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(MaxCol&nbsp;==&nbsp;oColor.green) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Hue&nbsp;=&nbsp;60.0&nbsp;*&nbsp;</span><span class="keyword">double</span><span>(oColor.blue&nbsp;-&nbsp;oColor.red)&nbsp;/&nbsp;(MaxCol&nbsp;-&nbsp;MinCol)&nbsp;+&nbsp;120; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(MaxCol&nbsp;==&nbsp;oColor.blue) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Hue&nbsp;=&nbsp;60.0&nbsp;*&nbsp;</span><span class="keyword">double</span><span>(oColor.red&nbsp;-&nbsp;oColor.green)&nbsp;/&nbsp;(MaxCol&nbsp;-&nbsp;MinCol)&nbsp;+&nbsp;240; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(CurrentHSLChannel&nbsp;==&nbsp;LCHannelHSL&nbsp;||&nbsp;CurrentHSLChannel&nbsp;==&nbsp;SCHannelHSL&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;||&nbsp;CurrentHSLChannel&nbsp;==&nbsp;HSLCHannelHSL) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lightness&nbsp;=&nbsp;((</span><span class="keyword">double</span><span>)MaxCol/255&nbsp;+&nbsp;(</span><span class="keyword">double</span><span>)MinCol/255)&nbsp;/&nbsp;2; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(CurrentHSLChannel&nbsp;==&nbsp;SCHannelHSL&nbsp;||&nbsp;CurrentHSLChannel&nbsp;==&nbsp;HSLCHannelHSL) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(lightness&nbsp;==&nbsp;0&nbsp;||&nbsp;MaxCol&nbsp;==&nbsp;MinCol) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;saturation&nbsp;=&nbsp;0.0; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>(lightness&nbsp;&lt;=&nbsp;0.5&nbsp;&amp;&amp;&nbsp;lightness&nbsp;&gt;&nbsp;0) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;saturation&nbsp;=&nbsp;</span><span class="keyword">double</span><span>(MaxCol&nbsp;-&nbsp;MinCol)&nbsp;/&nbsp;(MaxCol&nbsp;+&nbsp;MinCol); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>(lightness&nbsp;&gt;&nbsp;0.5) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;saturation&nbsp;=&nbsp;</span><span class="keyword">double</span><span>(MaxCol&nbsp;-&nbsp;MinCol)&nbsp;/&nbsp;(&nbsp;510&nbsp;-&nbsp;(MaxCol&nbsp;+&nbsp;MinCol)); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(CurrentHSLChannel&nbsp;==&nbsp;HSLCHannelHSL) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rColor.red&nbsp;&nbsp;&nbsp;=&nbsp;Hue&nbsp;/&nbsp;360.0&nbsp;*&nbsp;255&nbsp;+&nbsp;VariedHue; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rColor.green&nbsp;=&nbsp;saturation&nbsp;*&nbsp;255&nbsp;+&nbsp;VariedSaturation;&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rColor.blue&nbsp;&nbsp;=&nbsp;lightness&nbsp;*&nbsp;255&nbsp;+&nbsp;VariedLightness; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span>&nbsp;Col; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(CurrentHSLChannel&nbsp;==&nbsp;HCHannelHSL) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Col&nbsp;=&nbsp;Hue&nbsp;/&nbsp;360.0&nbsp;*&nbsp;255&nbsp;+&nbsp;VariedHue; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(CurrentHSLChannel&nbsp;==&nbsp;SCHannelHSL) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Col&nbsp;=&nbsp;saturation&nbsp;*&nbsp;255&nbsp;+&nbsp;VariedSaturation; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(CurrentHSLChannel&nbsp;==&nbsp;LCHannelHSL) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Col&nbsp;=&nbsp;lightness&nbsp;*&nbsp;255&nbsp;+&nbsp;VariedLightness; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rColor.</span><span class="keyword">set</span><span>(Col,&nbsp;Col,&nbsp;Col); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rColor.SetRange(0,&nbsp;255); </span></li>    <li><span>} </span></li>    <li class="alt">&nbsp;</li>    <li><span class="keyword">void</span><span>&nbsp;FormatHSLConvert::HSLToRGBConvert(FilterColor&nbsp;oColor,&nbsp;FilterColor&amp;&nbsp;rColor) </span></li>    <li class="alt"><span>{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//oColor.red&nbsp;&nbsp;&nbsp;-&nbsp;oColor.Hue </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//oColor.green&nbsp;-&nbsp;oColor.saturation </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//oColor.blue&nbsp;&nbsp;-&nbsp;oColor.lightness </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(oColor.green&nbsp;==&nbsp;0) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rColor.</span><span class="keyword">set</span><span>(oColor.blue,&nbsp;oColor.blue,&nbsp;oColor.blue); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">double</span><span>&nbsp;Hue&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;(</span><span class="keyword">double</span><span>)oColor.red&nbsp;/&nbsp;255.0;&nbsp;</span><span class="comment">//色相&nbsp;(原0-360，规范化到0&nbsp;-1&nbsp;) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">double</span><span>&nbsp;saturation&nbsp;=&nbsp;(</span><span class="keyword">double</span><span>)oColor.green&nbsp;/&nbsp;255.0;&nbsp;</span><span class="comment">//饱和度&nbsp;(0，1) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">double</span><span>&nbsp;lightness&nbsp;&nbsp;=&nbsp;(</span><span class="keyword">double</span><span>)oColor.blue&nbsp;/&nbsp;255.0;&nbsp;&nbsp;</span><span class="comment">//亮度&nbsp;(0,1) </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">double</span><span>&nbsp;mp&nbsp;=&nbsp;0.0,&nbsp;mq&nbsp;=&nbsp;0.0; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">double</span><span>&nbsp;tRGB[3]; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span>&nbsp;i&nbsp;=&nbsp;0; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(lightness&nbsp;&lt;&nbsp;0.5) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mq&nbsp;=&nbsp;lightness&nbsp;*&nbsp;(1&nbsp;+&nbsp;saturation); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mq&nbsp;=&nbsp;lightness&nbsp;+&nbsp;saturation&nbsp;-&nbsp;(lightness&nbsp;*&nbsp;saturation); </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mp&nbsp;=&nbsp;2&nbsp;*&nbsp;lightness&nbsp;-&nbsp;mq; </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tRGB[0]&nbsp;=&nbsp;Hue&nbsp;+&nbsp;0.333333;&nbsp;&nbsp;</span><span class="comment">//&nbsp;tR </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tRGB[1]&nbsp;=&nbsp;Hue;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;tG </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tRGB[2]&nbsp;=&nbsp;Hue&nbsp;-&nbsp;0.333333;&nbsp;&nbsp;</span><span class="comment">//&nbsp;tB </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;3;&nbsp;++i) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(tRGB[i]&nbsp;&lt;&nbsp;0) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tRGB[i]&nbsp;=&nbsp;tRGB[i]&nbsp;+&nbsp;1.0; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>(tRGB[i]&nbsp;&gt;&nbsp;1) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tRGB[i]&nbsp;=&nbsp;tRGB[i]&nbsp;-&nbsp;1.0; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(tRGB[i]&nbsp;&lt;&nbsp;0.166666) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tRGB[i]&nbsp;=&nbsp;mp&nbsp;+&nbsp;((mq&nbsp;-&nbsp;mp)&nbsp;*&nbsp;6&nbsp;*&nbsp;tRGB[i]); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>(tRGB[i]&nbsp;&gt;=&nbsp;0.166666&nbsp;&amp;&amp;&nbsp;tRGB[i]&nbsp;&lt;&nbsp;0.5) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tRGB[i]&nbsp;=&nbsp;mq; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>(tRGB[i]&nbsp;&gt;=&nbsp;0.5&nbsp;&amp;&amp;&nbsp;tRGB[i]&nbsp;&lt;&nbsp;0.666666) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tRGB[i]&nbsp;=&nbsp;mp&nbsp;+&nbsp;((mq&nbsp;-&nbsp;mp)&nbsp;*&nbsp;6&nbsp;*&nbsp;(0.666666&nbsp;-&nbsp;tRGB[i])); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tRGB[i]&nbsp;=&nbsp;mp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rColor.</span><span class="keyword">set</span><span>(tRGB[0]*&nbsp;255,&nbsp;tRGB[1]*&nbsp;255,&nbsp;tRGB[2]*&nbsp;255); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rColor.SetRange(0,&nbsp;255); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt">&nbsp;</li>    <li><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>void FormatHSLConvert::RGBToHSLConvert(FilterColor oColor, FilterColor&amp; rColor)<br/>{<br/>	    UCHAR MaxCol = oColor.red;<br/>		UCHAR MinCol = oColor.red;			<br/><br/>		if(oColor.green &gt; MaxCol)<br/>              MaxCol = oColor.green;<br/>		else<br/>			  MinCol = oColor.green;<br/>		<br/>		if(oColor.blue &gt; MaxCol)<br/>              MaxCol = oColor.blue;<br/>		if(oColor.blue &lt; MinCol)<br/>			  MinCol = oColor.blue;<br/><br/>		double Hue = 0.0; //色相 (0-360)<br/>		double saturation = 0.0; //饱和度 (0，1)<br/>		double lightness = 0.0;  //亮度 (0,1)<br/><br/>		if(CurrentHSLChannel == HCHannelHSL || CurrentHSLChannel == HSLCHannelHSL)<br/>		{<br/>		if(MaxCol == MinCol)<br/>			Hue = 0;<br/>		else <br/>			if(MaxCol == oColor.red &amp;&amp; oColor.green &gt;= oColor.blue)<br/>              Hue = 60.0 * double(oColor.green - oColor.blue) / (MaxCol - MinCol);<br/>		else <br/>			if(MaxCol == oColor.red &amp;&amp; oColor.green &lt; oColor.blue)<br/>              Hue = 60.0 * double(oColor.green - oColor.blue) / (MaxCol - MinCol) + 360;<br/>		else <br/>			if(MaxCol == oColor.green)<br/>              Hue = 60.0 * double(oColor.blue - oColor.red) / (MaxCol - MinCol) + 120;<br/>		else <br/>			if(MaxCol == oColor.blue)<br/>              Hue = 60.0 * double(oColor.red - oColor.green) / (MaxCol - MinCol) + 240;<br/>		}<br/><br/>		if(CurrentHSLChannel == LCHannelHSL || CurrentHSLChannel == SCHannelHSL <br/>                     || CurrentHSLChannel == HSLCHannelHSL)<br/>		{<br/>           lightness = ((double)MaxCol/255 + (double)MinCol/255) / 2;<br/>		}<br/><br/>		if(CurrentHSLChannel == SCHannelHSL || CurrentHSLChannel == HSLCHannelHSL)<br/>		{<br/>		   if(lightness == 0 || MaxCol == MinCol)<br/>			  saturation = 0.0;<br/>		   else if(lightness &lt;= 0.5 &amp;&amp; lightness &gt; 0)<br/>			  saturation = double(MaxCol - MinCol) / (MaxCol + MinCol);<br/>		   else if(lightness &gt; 0.5)<br/>			  saturation = double(MaxCol - MinCol) / ( 510 - (MaxCol + MinCol));<br/>		}<br/><br/>	  if(CurrentHSLChannel == HSLCHannelHSL)<br/>	  {<br/>	   rColor.red   = Hue / 360.0 * 255 + VariedHue;<br/>	   rColor.green = saturation * 255 + VariedSaturation;	<br/>	   rColor.blue  = lightness * 255 + VariedLightness;<br/>	  }<br/>	  else<br/>	  { <br/>		  int Col;<br/>		  if(CurrentHSLChannel == HCHannelHSL)<br/>	          Col = Hue / 360.0 * 255 + VariedHue;<br/>		  else <br/>			  if(CurrentHSLChannel == SCHannelHSL)<br/>		      Col = saturation * 255 + VariedSaturation;<br/>	      else <br/>			  if(CurrentHSLChannel == LCHannelHSL)<br/>              Col = lightness * 255 + VariedLightness;<br/><br/>	      rColor.set(Col, Col, Col);<br/>	  }<br/><br/>	 rColor.SetRange(0, 255);<br/>}<br/><br/>void FormatHSLConvert::HSLToRGBConvert(FilterColor oColor, FilterColor&amp; rColor)<br/>{<br/>	//oColor.red   - oColor.Hue<br/>	//oColor.green - oColor.saturation<br/>	//oColor.blue  - oColor.lightness<br/><br/>	if(oColor.green == 0)<br/>         rColor.set(oColor.blue, oColor.blue, oColor.blue);<br/>	else<br/>	{	<br/>        double Hue        = (double)oColor.red / 255.0; //色相 (原0-360，规范化到0 -1 )<br/>		double saturation = (double)oColor.green / 255.0; //饱和度 (0，1)<br/>		double lightness  = (double)oColor.blue / 255.0;  //亮度 (0,1)<br/><br/>		double mp = 0.0, mq = 0.0;<br/>		double tRGB[3];<br/>		int i = 0;<br/><br/>		if(lightness &lt; 0.5)<br/>		  mq = lightness * (1 + saturation);<br/>		else<br/>		  mq = lightness + saturation - (lightness * saturation);<br/><br/>		mp = 2 * lightness - mq;<br/><br/>		tRGB[0] = Hue + 0.333333;  // tR<br/>		tRGB[1] = Hue;             // tG<br/>		tRGB[2] = Hue - 0.333333;  // tB<br/><br/>		for(i = 0; i &lt; 3; ++i)<br/>		{<br/>		if(tRGB[i] &lt; 0)<br/>			tRGB[i] = tRGB[i] + 1.0;<br/>		else if(tRGB[i] &gt; 1)<br/>			tRGB[i] = tRGB[i] - 1.0;<br/><br/>		if(tRGB[i] &lt; 0.166666)<br/>            tRGB[i] = mp + ((mq - mp) * 6 * tRGB[i]);<br/>		else if(tRGB[i] &gt;= 0.166666 &amp;&amp; tRGB[i] &lt; 0.5)<br/>            tRGB[i] = mq;<br/>		else if(tRGB[i] &gt;= 0.5 &amp;&amp; tRGB[i] &lt; 0.666666)<br/>            tRGB[i] = mp + ((mq - mp) * 6 * (0.666666 - tRGB[i]));<br/>		else<br/>            tRGB[i] = mp;<br/>		}<br/><br/>		rColor.set(tRGB[0]* 255, tRGB[1]* 255, tRGB[2]* 255);<br/>		rColor.SetRange(0, 255);<br/>	}<br/><br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>说到底，转换的原理，那些深奥的东西我们没有必要知道，只要知道具体的转换公式，一切都很简单了。</p><p>程序截图：<br /><br/>[若看不到本博客图片，可参照此帖，简单的复原法：[<a target="_blank" href="http://www.zwqxin.com/archives/Way/show-all-picture.html">显示本站所有图片</a>] ]</p><p style="text-align: center"><a href="http://www.zwqxin.com/archives/image-processing/image-hsl-hsv.html"><img alt="图像空间HSL/HSV  www.zwqxin.com" src="http://lh4.ggpht.com/_lYWT-2PnV0s/Sd_-5V3LQBI/AAAAAAAAAXI/S4XICS3_y6s/s800/2.jpg" /></a><br /><br/>(原图片)</p><div style="margin: 0cm 0cm 0pt 23.25pt; text-indent: -18pt; text-align: center"><strong><span><span style="font: 7pt 'Times New Roman'">&nbsp;</span></span>HSL/HSV空间 [FormatHSLConvert&nbsp;&amp;&nbsp;FormatHSVConvert]</strong></div><div style="margin: 0cm 0cm 0pt 5.25pt">[注：以下展示，为在HSL空间里修改参量，在RGB空间里查看结果]</div><div style="margin: 0cm 0cm 0pt 5.25pt" align="center"><a href="http://www.zwqxin.com/archives/image-processing/image-hsl-hsv.html"><img alt="图像空间HSL/HSV  www.zwqxin.com" src="http://lh5.ggpht.com/_lYWT-2PnV0s/SnMfVk5b6eI/AAAAAAAAAtU/skMdXJG4vhQ/s800/2009730Snap.jpg" /></a><br /><br/>（在HSL空间，单独提高色相H分量）</div><div style="margin: 0cm 0cm 0pt 5.25pt" align="center"><a href="http://www.zwqxin.com/archives/image-processing/image-hsl-hsv.html"><img alt="图像空间HSL/HSV  www.zwqxin.com" src="http://lh5.ggpht.com/_lYWT-2PnV0s/SnMfWjvDHGI/AAAAAAAAAtc/JylUQeI2_7k/s800/2009730Snap-1.jpg" /></a><br /><br/>（在HSL空间，单独提高饱和度S分量）</div><div style="margin: 0cm 0cm 0pt 5.25pt" align="center"><a href="http://www.zwqxin.com/archives/image-processing/image-hsl-hsv.html"><img alt="图像空间HSL/HSV  www.zwqxin.com" src="http://lh3.ggpht.com/_lYWT-2PnV0s/SnMfUUtGRcI/AAAAAAAAAtE/o3bF9_CKEcQ/s800/2009730Snap-2.jpg" /></a><br /><br/>（在HSL空间，单独提高亮度L分量）</div><div style="margin: 0cm 0cm 0pt 5.25pt" align="center"><a href="http://www.zwqxin.com/archives/image-processing/image-hsl-hsv.html"><img style="width: 450px; height: 153px" height="141" alt="图像空间HSL/HSV  www.zwqxin.com" width="424" src="http://lh5.ggpht.com/_lYWT-2PnV0s/SnMfWCNRG0I/AAAAAAAAAtY/f7dSxmjiteI/s800/2009730Snap-3.jpg" /></a><br /><br/>[当然可随意自由转换空间，如上为保持HSL空间的修改回到RGB空间，查看R/G分量]</div><div style="margin: 0cm 0cm 0pt 5.25pt">[注：以下展示，为在HSV空间里修改参量，在RGB空间里查看结果]</div><div style="margin: 0cm 0cm 0pt 5.25pt" align="center"><a href="http://www.zwqxin.com/archives/image-processing/image-hsl-hsv.html"><img alt="图像空间HSL/HSV  www.zwqxin.com" src="http://lh5.ggpht.com/_lYWT-2PnV0s/SnMfUmWTyVI/AAAAAAAAAtI/rb7WO1N5dog/s800/2009730Snap-4.jpg" /></a><br /><br/>（在HSV空间，单独降低色相H分量）</div><div style="margin: 0cm 0cm 0pt 5.25pt" align="center"><a href="http://www.zwqxin.com/archives/image-processing/image-hsl-hsv.html"><img alt="图像空间HSL/HSV  www.zwqxin.com" src="http://lh6.ggpht.com/_lYWT-2PnV0s/SnMfVN9Kw8I/AAAAAAAAAtM/cbUg8QRwxCc/s800/2009730Snap-5.jpg" /></a><br /><br/>（在HSV空间，单独降低饱和度S分量[注意此S不同上S]）</div><div style="margin: 0cm 0cm 0pt 5.25pt" align="center"><a href="http://www.zwqxin.com/archives/image-processing/image-hsl-hsv.html"><img alt="图像空间HSL/HSV  www.zwqxin.com" src="http://lh5.ggpht.com/_lYWT-2PnV0s/SnMfW0ReWaI/AAAAAAAAAtg/txZXzZFIHSE/s800/2009730Snap-6.jpg" /></a><br /><br/>（在HSV空间，单独降低&ldquo;值&rdquo;V分量）</div><div style="margin: 0cm 0cm 0pt 5.25pt" align="center"><a href="http://www.zwqxin.com/archives/image-processing/image-hsl-hsv.html"><img height="147" alt="图像空间HSL/HSV  www.zwqxin.com" width="424" src="http://lh5.ggpht.com/_lYWT-2PnV0s/SnMfVRy4RSI/AAAAAAAAAtQ/wmRwLiDHQCs/s800/2009730Snap-7.jpg" /></a><br /><br/>[当然可随意在任何空间查看谱图或单独查看某一分量谱图，如上为HSV空间和HSV空间的S分量查看图（按灰度展示）]</div>]]></description><category>图像处理</category><comments>http://www.zwqxin.com/archives/image-processing/image-hsl-hsv.html#comment</comments><wfw:comment>http://www.zwqxin.com/</wfw:comment><wfw:commentRss>http://www.zwqxin.com/feed.asp?cmt=63</wfw:commentRss><trackback:ping>http://www.zwqxin.com/cmd.asp?act=tb&amp;id=63&amp;key=1a73b1c6</trackback:ping></item><item><title>快速傅立叶变换</title><author>a@b.com (zwqxin)</author><link>http://www.zwqxin.com/archives/image-processing/dif-fast-fourier.html</link><pubDate>Tue, 26 May 2009 20:14:43 +0800</pubDate><guid>http://www.zwqxin.com/archives/image-processing/dif-fast-fourier.html</guid><description><![CDATA[<p>傅立叶变换，在图形学上貌似应用不怎么多，海水波浪的运动算法方面需要吧，然后不怎么见了。而在图像处理上，频域处理这个大领域，傅立叶变换可是算法基础口牙~&mdash;&mdash;<a href="http://www.zwqxin.com/">ZwqXin</a>.com</p><p>其实啊,这个傅立叶的实现同是为了作业,但就在形态学处理之前。之所以迟迟不记录在博，主要还是因为自己对其原理还是云里雾里的。嘛~有时候写多了日志就有种&ldquo;什么东西要说就得说明白啊&rdquo;的错觉。其实嘛，本博客也不是什么传道授业解惑的地方，只是写着写着就有点忘记初衷了。</p><p>废话太多了。其实我只想说，找傅立叶原理算法的同学，接下来这篇文章没啥太大价值的。姑且当作一个被傅立叶实现搞晕了一两天的傻子的自言自语，恩，这里是我的记录簿。</p><p>最初被傅立叶缠上应该是上自动控制原理的时候吧，印象大概就是时域下的东西转去频率域吧，然后扔出一个复杂的公式。都知道，连续波都可以由正弦搅成。那么，代表任意连续波的函数由正弦函数搅成如何呢？正弦波状态特征是频率，振幅，相位吧，那么，一段时间内的波的函数形式分成几种不同周期（不同频率）的正弦表示的&ldquo;有机组合&rdquo;，就是所谓的时域 - 频域转换了。这很有用吧，在很多领域，包括自动控制.....不过其实我不太清楚具体起来是什么用，包括自动控制领域。</p><p>而在图像上的应用，其实也就是空间域 - 频率域。一张500*400的灰度图像，不妨假想为一张有500*400个方格的地形图（啥叫3D中毒...），某坐标处的灰度自然就是地形在该点的高度了（当然，没可能就像一张地形灰度图那样能形成比较连续的地形）。图像是按像素记数的，每个相邻像素的灰度或多或少都有个差值，形成一个梯度&mdash;&mdash;关键就在于这个&ldquo;或多或少&rdquo;上。现在，忽略相邻像素灰度间的插值等问题，就有一个地形出来了，地形当然有陡坡有缓坡（正是由梯度造成）&mdash;&mdash;这就是我们要处理的空间波了。傅立叶变换功效在于，变换结果是频率（梯度）&mdash;&mdash;经过变换所得的是一堆按灰度大小聚集的新像素（总的像素数量不变，但意义改变了，反映的是梯度而不是原来的&ldquo;灰度&rdquo;，而且与原来的空间位置无关）。</p><p>该看什么呢？就看&ldquo;聚集的情况&rdquo;（如，图中的白色区域和黑色区域，注意，它们没有明显的分界，因此只能直观去感受~）对于中部高亮区域，反映了与相邻像素梯度比较大的像素量；周围的黑色区域，反映了与相邻像素梯度比较小的像素量。这么说，就是图像中的高频部分和低频部分了&mdash;&mdash;图像中的边缘处就是最直观的&ldquo;高频部分&rdquo;了。至于要分离出高频和低频有什么用，那就是频率域处理的事情了。傅立叶变换作用就只是生成一张这样的图以把高低频部分分开。</p><p style="text-align: center"><img alt="" src="http://read.pudn.com/downloads70/sourcecode/graph/texture_mapping/252658/傅立叶变换/测试图片/傅立叶变换结果__.jpg" /></p><p>图像中离散的像素可以认为是二维波的采样点。因此对每行进行一维离散傅立叶变换，再在此基础上对每列进行一维离散傅立叶变换，则能得出两个纬度结合的傅立叶频率图（如上）。离散傅立叶变换的计算形式对计算机而言太残酷了（前面说了，式子复杂得很），因此就有人发明出快速傅立叶变换这种东西。</p><p>最容易理解的是&ldquo;基-2&rdquo;的快速傅立叶，因为它的采样点尺寸是2的N次方（N&gt; 0），能直接执行蝶形算法。一般图像长宽都不一定满足&mdash;&mdash;因此比较受限。补或截都是迫不得已的方法。N为合数的 FFT 算法 （混合基）可以不受限，不过复杂死了，我不想去深究。对&ldquo;基-2&rdquo;的快速傅立叶，又分为&ldquo;时域抽取基-2&rdquo;和&ldquo;频域抽取基-2&rdquo;，其实差不多，不过中间的步骤顺序有点差异而已（前者还要倒码之类的）。</p><p>我用的是&ldquo;频域抽取基-2快速傅立叶&rdquo;（DIF-FFT）。算法推导，诸君在网络上找找吧，反正结论就是一个符合蝶形算法的形式。</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt">&nbsp;</li>    <li><span class="comment">//&nbsp;&nbsp;DIT&nbsp;&ndash;&nbsp;FFT&nbsp;[频域抽取基-2FFT]&nbsp;蝶形算法： </span></li>    <li class="alt"><span class="keyword">double</span><span>&nbsp;angle; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(i&nbsp;&nbsp;=&nbsp;&nbsp;0;&nbsp;&nbsp;i&nbsp;&nbsp;&lt;&nbsp;&nbsp;Num&nbsp;/&nbsp;2;&nbsp;&nbsp;i++)&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;angle&nbsp;&nbsp;=&nbsp;&nbsp;-i&nbsp;&nbsp;*&nbsp;&nbsp;PI&nbsp;&nbsp;*&nbsp;&nbsp;2&nbsp;&nbsp;/&nbsp;&nbsp;Num;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(!InverseTranform) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;W[i]&nbsp;&nbsp;=&nbsp;&nbsp;complex&nbsp;&lt;</span><span class="keyword">double</span><span>&gt;&nbsp;&nbsp;(cos(angle),&nbsp;&nbsp;sin(angle));&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;W[i]&nbsp;&nbsp;=&nbsp;&nbsp;complex&nbsp;&lt;</span><span class="keyword">double</span><span>&gt;&nbsp;&nbsp;(cos(angle),&nbsp;&nbsp;-sin(angle));&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp; </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span>&nbsp;Apartsize&nbsp;=&nbsp;0,&nbsp;halfPartsize&nbsp;=&nbsp;0,&nbsp;Partoffset&nbsp;=&nbsp;0; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(k&nbsp;=&nbsp;0;&nbsp;k&nbsp;&lt;&nbsp;r;&nbsp;k++) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(j&nbsp;=&nbsp;0;&nbsp;j&nbsp;&lt;&nbsp;(1&lt;&lt;k);&nbsp;j++) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Apartsize&nbsp;=&nbsp;1&nbsp;&lt;&lt;&nbsp;(r&nbsp;-&nbsp;k); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;halfPartsize&nbsp;=&nbsp;Apartsize&nbsp;/&nbsp;2; </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;halfPartsize;&nbsp;i++) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Partoffset&nbsp;=&nbsp;j&nbsp;*&nbsp;Apartsize; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;X2[i&nbsp;+&nbsp;Partoffset]&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;X1[i&nbsp;+&nbsp;Partoffset]&nbsp;+&nbsp;X1[i&nbsp;+&nbsp;Partoffset&nbsp;+&nbsp;halfPartsize]; </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;X2[i&nbsp;+&nbsp;Partoffset&nbsp;+&nbsp;halfPartsize]&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;(X1[i&nbsp;+&nbsp;Partoffset]&nbsp;-&nbsp;X1[i&nbsp;+&nbsp;Partoffset&nbsp;+&nbsp;halfPartsize])&nbsp;*&nbsp;W[i&nbsp;*&nbsp;(1&lt;&lt;k)]; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;X&nbsp;=&nbsp;X1;</span><span class="comment">// </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;X1&nbsp;=X2; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;X2&nbsp;=&nbsp;X;</span><span class="comment">// </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt">&nbsp;</li>    <li><span class="comment">//到二维，横竖方向各转一次： </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(j&nbsp;=&nbsp;0;&nbsp;j&nbsp;&lt;Exheight;&nbsp;++j) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DifFastFourierTransform1D(&amp;input[j&nbsp;*&nbsp;Exwidth],&nbsp;&amp;output[j&nbsp;*&nbsp;Exwidth],&nbsp;Exwtimes); </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(j&nbsp;=&nbsp;0;&nbsp;j&nbsp;&lt;Exheight;&nbsp;++j) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;Exwidth;&nbsp;++i) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;input[i&nbsp;+&nbsp;j&nbsp;*&nbsp;Exwidth]&nbsp;=&nbsp;output[j&nbsp;+&nbsp;i&nbsp;*&nbsp;Exheight]; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;Exwidth;&nbsp;++i) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DifFastFourierTransform1D(&amp;input[i&nbsp;*&nbsp;Exheight],&nbsp;&amp;output[i&nbsp;*&nbsp;Exheight],&nbsp;Exhtimes); </span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>//	DIT &ndash; FFT [频域抽取基-2FFT] 蝶形算法：<br/>double angle;<br/>	for(i  =  0;  i  &lt;  Num / 2;  i++)    <br/>	{    <br/>	  angle  =  -i  *  PI  *  2  /  Num;    <br/>	  if(!InverseTranform)<br/>		  W[i]  =  complex &lt;double&gt;  (cos(angle),  sin(angle)); <br/>	  else<br/>		  W[i]  =  complex &lt;double&gt;  (cos(angle),  -sin(angle)); <br/>	 } <br/><br/>	int Apartsize = 0, halfPartsize = 0, Partoffset = 0;<br/>	for(k = 0; k &lt; r; k++)<br/>	{<br/>		for(j = 0; j &lt; (1&lt;&lt;k); j++)<br/>		{<br/>			Apartsize = 1 &lt;&lt; (r - k);<br/>			halfPartsize = Apartsize / 2;<br/><br/>			for(i = 0; i &lt; halfPartsize; i++)<br/>			{<br/>				Partoffset = j * Apartsize;<br/>				X2[i + Partoffset] <br/>					= X1[i + Partoffset] + X1[i + Partoffset + halfPartsize];<br/><br/>				X2[i + Partoffset + halfPartsize] <br/>				    = (X1[i + Partoffset] - X1[i + Partoffset + halfPartsize]) * W[i * (1&lt;&lt;k)];<br/>			}<br/>		}<br/>        X = X1;//<br/>		X1 =X2;<br/>        X2 = X;//<br/>	}<br/><br/>//到二维，横竖方向各转一次：<br/>	for(j = 0; j &lt;Exheight; ++j)<br/>		DifFastFourierTransform1D(&amp;input[j * Exwidth], &amp;output[j * Exwidth], Exwtimes);<br/><br/>	for(j = 0; j &lt;Exheight; ++j)<br/>	  for(i = 0; i &lt;Exwidth; ++i)<br/>          input[i + j * Exwidth] = output[j + i * Exheight];<br/><br/>	 for(i = 0; i &lt;Exwidth; ++i)<br/>		  DifFastFourierTransform1D(&amp;input[i * Exheight], &amp;output[i * Exheight], Exhtimes);<br/></pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>另外，对彩色图片，一般是针对每个颜色通道都做一个快速傅立叶变换哦。</p><p>再另外，其实傅立叶变换除了可以生成频率域图，还能生成相位图哦。只是输出的时候输出结果的振幅就可了，相位图能反映出梯度点在整张图片上的方位呢~</p><p>最后一句话：反变换跟正变换差不多的。不多得结合频率域图和相位图的信息哦，才能还原。</p><p>程序截图：<br /><br/>[若看不到本博客图片，可参照此帖，简单的复原法：[<a target="_blank" href="http://www.zwqxin.com/archives/Way/show-all-picture.html">显示本站所有图片</a>] ]</p><div style="margin: 0cm 0cm 0pt 23.25pt; text-indent: -18pt; text-align: center"><strong><span><span style="font: 7pt 'Times New Roman'">&nbsp;</span></span>快速傅立叶变换[DifFFTProcess]</strong></div><div style="margin: 0cm 0cm 0pt 23.25pt; text-indent: -18pt; text-align: center">&nbsp;<a href="http://www.zwqxin.com/archives/image-processing/dif-fast-fourier.html"><img alt="快速傅立叶 www.zwqxin.com" src="http://lh5.ggpht.com/_lYWT-2PnV0s/SnMn4xTiEkI/AAAAAAAAAuI/rtE-sf-S-dI/s800/81Snap1.jpg" /></a><br /><br/>（原始图）</div><div style="margin: 0cm 0cm 0pt 23.25pt; text-indent: -18pt; text-align: center"><a href="http://www.zwqxin.com/archives/image-processing/dif-fast-fourier.html"><img style="width: 512px; height: 436px" height="435" alt="快速傅立叶 www.zwqxin.com" width="484" src="http://lh4.ggpht.com/_lYWT-2PnV0s/SnMn5aBvoCI/AAAAAAAAAuM/cvw9Hj8BabM/s800/81Snap.jpg" /></a><br /><br/>(Fourier)</div><div style="margin: 0cm 0cm 0pt 23.25pt; text-indent: -18pt; text-align: center"><br /><br/><a href="http://www.zwqxin.com/archives/image-processing/dif-fast-fourier.html"><img alt="快速傅立叶 www.zwqxin.com" src="http://lh3.ggpht.com/_lYWT-2PnV0s/SnMq1gDdaoI/AAAAAAAAAuo/V4bv_M9AAcc/s800/fullmental24.jpg" /></a><br /><br/>(彩色图，原图)<br /><br/><a href="http://www.zwqxin.com/archives/image-processing/dif-fast-fourier.html"><img alt="快速傅立叶 www.zwqxin.com" src="http://lh6.ggpht.com/_lYWT-2PnV0s/SnMn3tnw2qI/AAAAAAAAAt8/PMq_WcFt3jk/s800/81Snap-1.jpg" /></a></div><div style="margin: 0cm 0cm 0pt 23.25pt; text-indent: -18pt; text-align: center">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.zwqxin.com/archives/image-processing/dif-fast-fourier.html"><img style="width: 340px; height: 278px" height="287" alt="快速傅立叶 www.zwqxin.com" width="340" src="http://lh5.ggpht.com/_lYWT-2PnV0s/SnMn32Emy4I/AAAAAAAAAuA/JrLEDS7z5rQ/s800/81Snap-2.jpg" /></a><br /><br/>[对非2幂大小的某图像，按需（见对话框）取其各个角域。图上为变换（上）和反变换]</div><div style="margin: 0cm 0cm 0pt 23.25pt; text-indent: -18pt; text-align: center"><a href="http://www.zwqxin.com/archives/image-processing/dif-fast-fourier.html"><img height="221" alt="快速傅立叶 www.zwqxin.com" width="500" src="http://lh6.ggpht.com/_lYWT-2PnV0s/SnMn4bEJrzI/AAAAAAAAAuE/knvvf4BH18A/s800/81Snap-3.jpg" /></a><br /><br/>[按需察看彩色图的不同通道下的频域图，图左为上图的蓝色通道；图右：上图的相位图]</div>]]></description><category>图像处理</category><comments>http://www.zwqxin.com/archives/image-processing/dif-fast-fourier.html#comment</comments><wfw:comment>http://www.zwqxin.com/</wfw:comment><wfw:commentRss>http://www.zwqxin.com/feed.asp?cmt=55</wfw:commentRss><trackback:ping>http://www.zwqxin.com/cmd.asp?act=tb&amp;id=55&amp;key=49d593f8</trackback:ping></item><item><title>形态学运算小结</title><author>a@b.com (zwqxin)</author><link>http://www.zwqxin.com/archives/image-processing/morphologic-process.html</link><pubDate>Sat, 16 May 2009 08:38:22 +0800</pubDate><guid>http://www.zwqxin.com/archives/image-processing/morphologic-process.html</guid><description><![CDATA[<div v:shape="_x0000_s1026"><div>数学形态学(Mathematical Morphology)，是分析几何形状和结构的数学方法，包括膨胀、腐蚀、开、闭等运算。在图像处理里面，用于&ldquo;保持基本形状，去除不相关特征&rdquo;&mdash;&mdash;<a href="http://www.zwqxin.com/">ZwqXin</a>.com</div><p>什么意思呢？图像中有一个貌似正方形的区域，然后你通过处理，可以把&ldquo;貌似&rdquo;去掉；图像是一个不清晰的指纹，然后你通过处理，也把&ldquo;不&rdquo;字去掉；图像中有很多大小不一的圆形区域，然后你通过处理，找出大者祛除小者（去噪）......应用是很多的，但是没有一个完整的范畴，就是很多时候要做某种图像处理实际应用的时候，会想起它吧。</p></div><p>因为处理的都是二值图像（用来分清目标和背景），因此有必要预先把图片转化为8BIT的二值图像，便于处理操作。我是把黑色像素作为目标区域，白色像素作为背景的（当然可以反相）。</p><p>结构元素：</p><p>其实就是自己定义的一块像素区域，形态学处理的时候用这个元素与原图像作与或运算，生成结果。</p><p>膨胀：</p><p style="text-align: center"><img alt="" src="http://lh6.ggpht.com/_lYWT-2PnV0s/SlaavLx7UgI/AAAAAAAAAlA/sfdTyrHuz94/s800/rt.png.png" /></p><p>设A为原图像。B为结构元素（C为B的映像，没用），那么D的整块&ldquo;灰+黑&rdquo;区域就是新图像了（反映在图像里都是黑像素），可以看到图中用黑色标识的是&ldquo;膨胀&rdquo;出来的。它就是把结构元素B的中心（+）分别与原图像A&ldquo;重合&rdquo;，然后考虑结构元素里的&ldquo;中心以外&rdquo;的像素将在原图像中出现的位置，并与原图像该位置的像素进行0-1值的&ldquo;或运算&rdquo;（设黑色为0，白色为1，下同）</p><p>腐蚀：</p><p style="text-align: center"><img alt="" src="http://lh6.ggpht.com/_lYWT-2PnV0s/SlaauV1SkZI/AAAAAAAAAk4/aDzJFjiGlw0/s800/rt2.png.png" /></p><p>腐蚀的解释与膨胀相同，不过执行的是&ldquo;与运算&rdquo;。如图，最终图像是C中的纯黑色部分。由此可见膨胀与腐蚀的关系与不同点。</p><p>至于&ldquo;开&rdquo;与&ldquo;闭&rdquo;，无非是结合膨胀腐蚀的二次操作。前者是先腐蚀后膨胀，后者是先膨胀后腐蚀。要注意的是第一次是用结构元素，第二次则是用结构元素的倒置（相当于矩阵的转置咯，这是为了最后图像的&ldquo;平衡&rdquo;，如果两次用同一个的话，结果只会是等同双重&ldquo;膨胀&rdquo;或&ldquo;腐蚀&rdquo;。注意，概念跟&ldquo;映像&rdquo;是不同 di，见下图，元素中心也换位置了）：</p><p style="text-align: center"><img alt="" src="http://lh4.ggpht.com/_lYWT-2PnV0s/SlaiiLa5yDI/AAAAAAAAAlg/BI19xIQHP2Y/s800/rt5.PNG.png" /></p><p>开运算效果：<br /><br/>- 删除小物体； <br /><br/>&ndash;将物体拆分为小物体； <br /><br/>&ndash;平滑大物体边界而不明显改变它们的面积；</p><div class="O1" v:shape="_x0000_s1026"><div>闭运算效果：</div><div>&nbsp;- 填充物体的小洞；</div><div><div class="O1" v:shape="_x0000_s1026"><div>&ndash;连接相近的物体；</div><div>&ndash;平滑物体的边界而不明显改变它们的面积。</div><div style="text-align: center"><img alt="" src="http://lh5.ggpht.com/_lYWT-2PnV0s/Slaih5v7g7I/AAAAAAAAAlc/ub5xNUEhxMU/s400/rt4.png.png" /><br /><br/>（前者为&ldquo;开&rdquo;，后者为&ldquo;闭&rdquo;）</div><div>[注：以上图片多转自冈萨雷斯那本图像处理的书，老师的教学用PPT中截取]</div><div>&nbsp;</div><div>总的操作代码见下。</div></div></div></div><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">void</span><span>&nbsp;MorphologicProcess::BmpProcess8(OperateBMP&amp;&nbsp;image,&nbsp;</span><span class="keyword">int</span><span>&nbsp;Hdis,&nbsp;</span><span class="keyword">int</span><span>&nbsp;Wdis) </span></span></li>    <li><span>{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;GetMinimumStructureElement(MorphElement,&nbsp;LineGrid,&nbsp;StructLineGridX,&nbsp;StructLineGridY); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(MorphType&nbsp;==&nbsp;MorphErosion) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MorphDilaEroProcess(image,&nbsp;Hdis,&nbsp;Wdis,&nbsp;MorphErosion); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>(MorphType&nbsp;==&nbsp;MorphDilation) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MorphDilaEroProcess(image,&nbsp;Hdis,&nbsp;Wdis,&nbsp;MorphDilation); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>(MorphType&nbsp;==&nbsp;MorphOpen) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MorphDilaEroProcess(image,&nbsp;Hdis,&nbsp;Wdis,&nbsp;MorphErosion); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetRotInverseStructureElement(); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GenBinaryMorph(image); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MorphDilaEroProcess(image,&nbsp;Hdis,&nbsp;Wdis,&nbsp;MorphDilation); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetRotInverseStructureElement(); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>(MorphType&nbsp;==&nbsp;MorphClose) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MorphDilaEroProcess(image,&nbsp;Hdis,&nbsp;Wdis,&nbsp;MorphDilation); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetRotInverseStructureElement(); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GenBinaryMorph(image); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MorphDilaEroProcess(image,&nbsp;Hdis,&nbsp;Wdis,&nbsp;MorphErosion); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetRotInverseStructureElement(); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt"><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>void MorphologicProcess::BmpProcess8(OperateBMP&amp; image, int Hdis, int Wdis)<br/>{<br/>    GetMinimumStructureElement(MorphElement, LineGrid, StructLineGridX, StructLineGridY);<br/>	                          <br/><br/>	if(MorphType == MorphErosion)<br/>	    MorphDilaEroProcess(image, Hdis, Wdis, MorphErosion);<br/>    else if(MorphType == MorphDilation)<br/>	    MorphDilaEroProcess(image, Hdis, Wdis, MorphDilation);<br/>	else if(MorphType == MorphOpen)<br/>	{<br/><br/>	    MorphDilaEroProcess(image, Hdis, Wdis, MorphErosion);<br/>		GetRotInverseStructureElement();<br/>		GenBinaryMorph(image);<br/>	    MorphDilaEroProcess(image, Hdis, Wdis, MorphDilation);<br/>		GetRotInverseStructureElement();<br/>	}<br/>	else if(MorphType == MorphClose)<br/>	{<br/>	    MorphDilaEroProcess(image, Hdis, Wdis, MorphDilation);<br/>		GetRotInverseStructureElement();<br/>		GenBinaryMorph(image);<br/>	    MorphDilaEroProcess(image, Hdis, Wdis, MorphErosion);<br/>		GetRotInverseStructureElement();<br/>	}<br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>在细节上不想多说了。难处不在形态学算法本身，而在&ldquo;自定义结构元素&rdquo;（MorphElement）这块，包括它与原图像进行形态学运算时必须要考虑的边界条件处理。有需要的同学可以留言给我[<a target="_blank" href="http://www.zwqxin.com">http://www.zwqxin.com</a>]。（当然如果自己连算法怎么样也不懂，只是为了交作业交任务而索要的话，恕不理会- -）</p><p style="text-align: center"><a target="_self" href="http://www.zwqxin.com/archives/image-processing/morphologic-process.html"><img alt="形态学处理 www.zwqxin.com" src="http://lh6.ggpht.com/_lYWT-2PnV0s/SlarRWnq25I/AAAAAAAAAl8/t29JWX9faGg/s800/tytry.PNG.png" /></a><br /><br/>(原始测试图片，处理对话框)<br /><br/><a target="_self" href="http://www.zwqxin.com/archives/image-processing/morphologic-process.html"><img alt="形态学处理 www.zwqxin.com" src="http://lh5.ggpht.com/_lYWT-2PnV0s/SlarRiec-7I/AAAAAAAAAmA/0e1XOz9STYw/s800/tytryr.PNG.png" /></a><br /><br/>(自定义任意的结构元素，约定中间红点为结构元素中心)<br /><br/><a target="_self" href="http://www.zwqxin.com/archives/image-processing/morphologic-process.html"><img alt="形态学处理 www.zwqxin.com" src="http://lh5.ggpht.com/_lYWT-2PnV0s/SlarR-XLE5I/AAAAAAAAAmE/YVAERMR5UtU/s800/dfff.PNG.png" /></a><br /><br/>(膨胀，处理结果图片)<br /><br/><a target="_self" href="http://www.zwqxin.com/archives/image-processing/morphologic-process.html"><img alt="形态学处理 www.zwqxin.com" src="http://lh3.ggpht.com/_lYWT-2PnV0s/SlarSMskhUI/AAAAAAAAAmI/X75v3tY8VOc/s800/gghhg.PNG.png" /></a><br /><br/>(任意定义新的结构元素，作为腐蚀例子)<br /><br/><a target="_self" href="http://www.zwqxin.com/archives/image-processing/morphologic-process.html"><img alt="形态学处理 www.zwqxin.com" src="http://lh5.ggpht.com/_lYWT-2PnV0s/SlarSQQNFYI/AAAAAAAAAmM/zTBilEGjUJ8/s800/ttt.PNG.png" /></a><br /><br/>(腐蚀，处理结果图片)</p><p>开和闭运算就不截图了，免得一篇日志里图太多。</p>]]></description><category>图像处理</category><comments>http://www.zwqxin.com/archives/image-processing/morphologic-process.html#comment</comments><wfw:comment>http://www.zwqxin.com/</wfw:comment><wfw:commentRss>http://www.zwqxin.com/feed.asp?cmt=54</wfw:commentRss><trackback:ping>http://www.zwqxin.com/cmd.asp?act=tb&amp;id=54&amp;key=1518b820</trackback:ping></item><item><title>VFW视频捕获之尝试</title><author>a@b.com (zwqxin)</author><link>http://www.zwqxin.com/archives/image-processing/vfw-capture-try.html</link><pubDate>Sun, 10 May 2009 13:45:36 +0800</pubDate><guid>http://www.zwqxin.com/archives/image-processing/vfw-capture-try.html</guid><description><![CDATA[<p>VFW，全名Video For Windows，是一种用于视频处理的工具包，也可以说是一种技术&mdash;&mdash;尽管从今天看来，这是一种简陋的工具包，落后的技术。&mdash;&mdash;<a href="http://www.zwqxin.com/">ZwqXin</a>.com</p><p>实现这个可以说完全由于之前某个作业的关系，当然，选择VFW而不是其他，仅仅是因为第一次GOOGLE&ldquo;视频捕获&rdquo;就找到它了。至于它怎么个落后法也不理了，反正顶多也就算入门吧，用点古老而简单的技术又如何？</p><p>视频捕捉卡是怎么一种概念呢？可以说是一种基于软件和硬件的模型，古老的VFW和新进的WDM就是这么两种模型。视频捕捉技术是怎么一种概念呢？可以说是一种用代码用API去&ldquo;贯通&rdquo;模型的&ldquo;方法&rdquo;，古老的VFW技术和不那么古老的DirectShow便是这么两种方法。VFW的体系结构缺乏为视频会议，电视浏览，视频区域捕获和VBI（Vertical&nbsp;Blanking&nbsp;Interval）数据流提供强而有效的支持[语自<a target="_blank" href="http://blog.csdn.net/suntaoznz/archive/2006/02/10/596180.aspx">Windows下WDM 视频捕获简介</a>]，因而现在只不过是在乖乖地等待被淘汰的命运而已。的确，微软也抛弃了的微软东西，很难翻盘了。</p><p>当然，知道这么&ldquo;让人无语&rdquo;的事实，是在完成程序发现捕获帧速好差，再次GOOGLE&ldquo;VFW&rdquo;的时候了。</p><p>遗憾没有找回当时给予我API讲述参照的VFW介绍文，这里写个新的吧：<a target="_blank" href="http://blog.csdn.net/suntaoznz/archive/2005/08/06/447067.aspx">VFW在windows下编程控制摄像头</a>&nbsp;。在这里能找到API的详细论述，我就无谓在此多费唇舌了。</p><p>什么是视频捕获呢？这是视频处理的第一步。假设你有部DV，你去乱拍摄些什么乱七八糟的东西，回家弄成视频，然后拿个播放器播放，这肯定算是&ldquo;视频捕获&rdquo;，不过中间牵涉的细节多着了。又譬如，你有个QQ，你去跟人家视频聊天，聊些什么乱七八糟的东西，然后对方的QQ要读取你发送过去的数据，变成可视化的视频形式展示给人家看，对方的QQ此时也肯定是在&ldquo;视频捕获&rdquo;，而且&ldquo;很单纯地只是读数据而已呀，你想什么！&rdquo;。又譬如，我把摄像头放电脑桌前，开着，让它摄些正经的东西，然后用VFW的API把所摄取到的正经的东西在电脑上呈现、播放，我也是在&ldquo;视频捕获&rdquo;，而且很正经。</p><p>具体呢？参考上面那篇文章应该能有所获，在WIN32中（MFC同理），通过<font face="Times New Roman" size="2">capCreateCaptureWindow 能直接生成一个捕获窗口，返回句柄。接下来就是把摄像头跟此窗口连接了&mdash;&mdash;摄像头在VFW中被抽象成一个&ldquo;驱动设备号&rdquo;，</font>用capDriverConnect（发送通过使用WM_CAP_DRIVR_CONNECT消息）就够了&mdash;&mdash;好了，捕捉完成。</p><p>：啥米？</p><p>：？就是这样啊.......</p><p>：&nbsp;(-_ -)||</p><p>：哦，当然了，还有几个设置用的系统对话框的调用。还有截图截声截AVI的API，啦啦啦。。。。</p><p>最后（喂喂！），我想说说截取图像的方法。在VFW里，某帧图像的捕获，可以用capFileSaveDIB把它弄到硬盘，也可以用capGrabFrameNoStop把它弄到剪切板。那么，如果我想实现通常的那种&ldquo;另存为&rdquo;的存储方式时怎么办呢？可以用剪切板编程：把该帧图像弄到剪切板后，把剪切板的内容从新弄成一张位图，再对该位图实施保存。</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">void</span><span>&nbsp;VideoCapture::SnatchImage(HWND&nbsp;ParentWnd,&nbsp;HDC&nbsp;DeviceDrawingContext) </span></span></li>    <li><span>{&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(!Connected) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ConnectToVideoDriver(ParentWnd,&nbsp;VideoID); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ISnatched&nbsp;&nbsp;=&nbsp;capGrabFrameNoStop(VideoWnd); </span></li>    <li class="alt">&nbsp;</li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(ISnatched) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(capEditCopy(VideoWnd))</span><span class="comment">//到剪贴板 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(MessageBox(NULL,&nbsp;</span><span class="string">&quot;已保存到系统剪贴板,显示于窗口不?\n[请在显示后再保存]&quot;</span><span>,&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="string">&quot;Snatch&nbsp;&nbsp;Succeeds&quot;</span><span>,&nbsp;MB_YESNO&nbsp;|&nbsp;MB_ICONINFORMATION)&nbsp;==&nbsp;IDYES) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(OpenClipboard(ParentWnd)) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;enImageShow(); </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RECT&nbsp;windowRect; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetClientRect(ParentWnd,&nbsp;&amp;windowRect); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BmpHandle&nbsp;=&nbsp;(HBITMAP)GetClipboardData(CF_BITMAP); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BmpDC&nbsp;=&nbsp;CreateCompatibleDC(DeviceDrawingContext); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SelectObject(BmpDC,&nbsp;BmpHandle); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BitBlt(DeviceDrawingContext,&nbsp;10,10,windowRect.right-20,&nbsp;windowRect.bottom-50,BmpDC,0,0,SRCCOPY); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CloseClipboard(); </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MessageBox(NULL,&nbsp;</span><span class="string">&quot;&nbsp;剪贴板有问题&nbsp;&quot;</span><span>,&nbsp;</span><span class="string">&quot;Clipboard&nbsp;&nbsp;Fails&quot;</span><span>,&nbsp;MB_OK&nbsp;|&nbsp;MB_ICONINFORMATION); </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>void VideoCapture::SnatchImage(HWND ParentWnd, HDC DeviceDrawingContext)<br/>{ <br/>	<br/>	if(!Connected)<br/>	{<br/>		ConnectToVideoDriver(ParentWnd, VideoID);<br/>		return;<br/>	}<br/>	else<br/>	   ISnatched  = capGrabFrameNoStop(VideoWnd);<br/><br/><br/>	if(ISnatched)<br/>	{<br/>		if(capEditCopy(VideoWnd))//到剪贴板<br/>			if(MessageBox(NULL, &quot;已保存到系统剪贴板,显示于窗口不?\n[请在显示后再保存]&quot;, <br/>				&quot;Snatch  Succeeds&quot;, MB_YESNO | MB_ICONINFORMATION) == IDYES)<br/>			{<br/>				if(OpenClipboard(ParentWnd))<br/>				{ <br/>                  enImageShow();<br/><br/>				  RECT windowRect;<br/>               	  GetClientRect(ParentWnd, &amp;windowRect);<br/>					  <br/><br/>                  BmpHandle = (HBITMAP)GetClipboardData(CF_BITMAP);<br/>				  BmpDC = CreateCompatibleDC(DeviceDrawingContext);<br/>				  SelectObject(BmpDC, BmpHandle);<br/>				  BitBlt(DeviceDrawingContext, 10,10,windowRect.right-20, windowRect.bottom-50,BmpDC,0,0,SRCCOPY);<br/>				  CloseClipboard();<br/><br/>				}<br/>				else<br/>		         MessageBox(NULL, &quot; 剪贴板有问题 &quot;, &quot;Clipboard  Fails&quot;, MB_OK | MB_ICONINFORMATION);<br/><br/>			}<br/><br/>	}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>上面最后效果只是显示出来，但是过程中生成了位图的句柄，位图资源DC（见[<a target="_blank" href="http://www.zwqxin.com/archives/image-processing/bmp-operate-copy-memory.html">认识HBITMAP与Bmp操作(整内存拷贝版)</a>] ），保存也不过是调用GetDIBits来从它们身上取得位图数据，填充填充BMP文件头之类的了。</p><p>最后给上DEMO，完结本文。<br /><br/><a target="_blank" href="http://code.google.com/p/lvideocapture/">ZwqXin 's LVideoCapture ver1.0</a>&nbsp;（进后，点download链接）</p>]]></description><category>图像处理</category><comments>http://www.zwqxin.com/archives/image-processing/vfw-capture-try.html#comment</comments><wfw:comment>http://www.zwqxin.com/</wfw:comment><wfw:commentRss>http://www.zwqxin.com/feed.asp?cmt=52</wfw:commentRss><trackback:ping>http://www.zwqxin.com/cmd.asp?act=tb&amp;id=52&amp;key=bef39eb7</trackback:ping></item><item><title>点与直方图处理的小结</title><author>a@b.com (zwqxin)</author><link>http://www.zwqxin.com/archives/image-processing/image-process-point-histogram.html</link><pubDate>Tue, 28 Apr 2009 18:46:29 +0800</pubDate><guid>http://www.zwqxin.com/archives/image-processing/image-process-point-histogram.html</guid><description><![CDATA[<p>点.直方图处理一般不涉及什么复杂算法，而且最大的特点是针对图像中每个像素处理而求取整体上的某种效果，或者需要对直方图作新映射。本文总结一下最近实现了的几种基本操作。&mdash;&mdash;<a href="http://www.zwqxin.com/">ZwqXin</a>.com</p><p>原始图像和直方图：</p><p style="text-align: center"><a target="_self" href="http://www.zwqxin.com/archives/image-processing/image-process-point-histogram.html"><img alt="点.直方图处理 - www.zwqxin.com" src="http://lh4.ggpht.com/_lYWT-2PnV0s/Sd_-5V3LQBI/AAAAAAAAAXI/S4XICS3_y6s/s800/2.jpg" /></a><br /><br/><a target="_self" href="http://www.zwqxin.com/archives/image-processing/image-process-point-histogram.html"><img alt="点.直方图处理 - www.zwqxin.com" src="http://lh3.ggpht.com/_lYWT-2PnV0s/Shv8wd2WskI/AAAAAAAAAY0/BGOT0xLDpb4/s800/Snap1.jpg" /></a></p><p>1. 黑白二值化</p><p>见[<a target="_blank" href="http://www.zwqxin.com/archives/image-processing/image-intensity-binarization.html">基于亮度的图像二值化处理</a>] 一文。图像需先转化为灰度图（亮度图），可调节参数是阈值位置，一般是中间值：127或128</p><p style="text-align: center"><a target="_self" href="http://www.zwqxin.com/archives/image-processing/image-process-point-histogram.html"><img alt="点.直方图处理 - www.zwqxin.com" src="http://lh4.ggpht.com/_lYWT-2PnV0s/Sd__EtMeKhI/AAAAAAAAAXY/e2Lvtm__snU/s800/22.jpg" /></a><br /><br/>&nbsp;</p><p>2.反相</p><p>比较简单，就是对灰度为X的像素，取结果为255-X就是了。图像可以转化为灰度图，或者单纯对彩色图每通道进行。</p><p style="text-align: center"><a target="_self" href="http://www.zwqxin.com/archives/image-processing/image-process-point-histogram.html"><img alt="点.直方图处理 - www.zwqxin.com" src="http://lh4.ggpht.com/_lYWT-2PnV0s/Shv8v7_5GVI/AAAAAAAAAYs/F1MjwvvoaQs/s800/Snap3.jpg" /></a><br /><br/><a target="_self" href="http://www.zwqxin.com/archives/image-processing/image-process-point-histogram.html"><img alt="点.直方图处理 - www.zwqxin.com" src="http://lh5.ggpht.com/_lYWT-2PnV0s/Shv8wMeVlAI/AAAAAAAAAYw/xasSUXX_cA0/s800/Snap4.jpg" /></a></p><p>3.灰度切割</p><p>也比较简单，不过就是指定图片内某一灰度值范围[A，B]内的像素&ldquo;可见&rdquo;而已。图像转化为灰度图后，接收A，B两个参数，并适当处理该范围外的像素怎么显示&mdash;&mdash;取决于应用，常见全黑，全白，或者低灰度部分黑化高灰度部分白化。</p><p style="text-align: center"><a target="_self" href="http://www.zwqxin.com/archives/image-processing/image-process-point-histogram.html"><img alt="点.直方图处理 - www.zwqxin.com" src="http://lh5.ggpht.com/_lYWT-2PnV0s/Shv8wvU39GI/AAAAAAAAAY4/OFN0p34p_Ms/s800/Snap6.jpg" /></a><br /><br/><a target="_self" href="http://www.zwqxin.com/archives/image-processing/image-process-point-histogram.html"><img alt="点.直方图处理 - www.zwqxin.com" src="http://lh6.ggpht.com/_lYWT-2PnV0s/Shv8xKUOjBI/AAAAAAAAAY8/bJ_FkKtCcNs/s800/Snap7.jpg" /></a></p><p>4.灰度拉伸（对比度拉伸）</p><p>对256个灰度级建立映射，常用线性变换（包括photoshop），看图就能明白是怎么回事了。</p><p style="text-align: center"><a target="_self" href="http://www.zwqxin.com/archives/image-processing/image-process-point-histogram.html"><img style="width: 303px; height: 186px" height="171" alt="点.直方图处理 - www.zwqxin.com" width="296" src="http://lh6.ggpht.com/_lYWT-2PnV0s/Shv8y-aLGuI/AAAAAAAAAZY/1MKaJJirnUs/s800/Snap14.jpg" /></a><a target="_self" href="http://www.zwqxin.com/archives/image-processing/image-process-point-histogram.html"><img style="width: 194px; height: 183px" height="218" alt="点.直方图处理 - www.zwqxin.com" width="258" src="http://lh6.ggpht.com/_lYWT-2PnV0s/Shv8zKQmgjI/AAAAAAAAAZc/kaTuPlohX0I/s800/Snap16.jpg" /></a><br /><br/>(原图转为灰度图后，对应的映射图)<br /><br/><a target="_self" href="http://www.zwqxin.com/archives/image-processing/image-process-point-histogram.html"><img style="width: 309px; height: 201px" height="218" alt="点.直方图处理 - www.zwqxin.com" width="328" src="http://lh6.ggpht.com/_lYWT-2PnV0s/Shv8zZ1tiyI/AAAAAAAAAZg/DDMNlHBT6ew/s800/Snap17.jpg" /></a><a target="_self" href="http://www.zwqxin.com/archives/image-processing/image-process-point-histogram.html"><img style="width: 200px; height: 195px" height="219" alt="点.直方图处理 - www.zwqxin.com" width="226" src="http://lh4.ggpht.com/_lYWT-2PnV0s/Shv8zlqmlmI/AAAAAAAAAZk/ah1LVwtBJxc/s800/Snap18.jpg" /></a><br /><br/>(中部斜率k&gt;1，对应的映射图)<br /><br/><a target="_self" href="http://www.zwqxin.com/archives/image-processing/image-process-point-histogram.html"><img style="width: 303px; height: 203px" height="218" alt="点.直方图处理 - www.zwqxin.com" width="328" src="http://lh4.ggpht.com/_lYWT-2PnV0s/Shv8z5jVPFI/AAAAAAAAAZo/NFP9kaFCJ3o/s800/Snap19.jpg" /></a><a target="_self" href="http://www.zwqxin.com/archives/image-processing/image-process-point-histogram.html"><img style="width: 211px; height: 202px" height="219" alt="点.直方图处理 - www.zwqxin.com" width="226" src="http://lh6.ggpht.com/_lYWT-2PnV0s/Shv80GOu-nI/AAAAAAAAAZs/faXh6tXlWY8/s800/Snap20.jpg" /></a><br /><br/>(中部斜率k&lt;1，对应的映射图)</p><p>同样给予一个灰度值范围[A，B]，另加一个中部斜率k。假设具有中间灰度（127或128）的像素保持恒定，那么：<br /><br/>对于中间部分（具有[A，B]灰度的像素）：斜率k小于1则整体变暗，大于1则整体变亮；<br /><br/>对于高灰度部分和低灰度部分：由A，B和k决定，在所有像素映射灰度[0，255]下，若k小于1，则高、低部分斜率大于1，映射灰度扩大；若k大于1则反之，出现&ldquo;暗部越暗，亮部越亮&rdquo;。</p><p>总体而言，对比度拉伸就是为了增强对比效应，突出图像中前背景之视差。</p><p>5.直方图均衡</p><p>我们希望一张图的像素应该尽量分布于各灰度间，也就是图像拥有一个均衡的直方图（直方图，横坐标为灰度，纵坐标为该灰度在图片中的相对数量）。但是很多图片的像素灰度集中于某一个局部，譬如集中于低灰度级处或高灰度级处，造成图片过暗或过亮。这时候重新分配一下灰度，把那些堆挤在一起的灰度线扩散到整个灰度范围。从某一意义来说，这也是增强图像对比度的一种方法。</p><p style="text-align: center"><a target="_self" href="http://www.zwqxin.com/archives/image-processing/image-process-point-histogram.html"><img alt="点.直方图处理 - www.zwqxin.com" src="http://lh3.ggpht.com/_lYWT-2PnV0s/Shv8xkWlBbI/AAAAAAAAAZA/VgLBTi2QsSw/s800/Snap8.jpg" /></a><br /><br/><a target="_self" href="http://www.zwqxin.com/archives/image-processing/image-process-point-histogram.html"><img alt="点.直方图处理 - www.zwqxin.com" src="http://lh6.ggpht.com/_lYWT-2PnV0s/Shv8xiqo4kI/AAAAAAAAAZE/_OU8zqmvQpc/s800/Snap9.jpg" /></a></p><p>算法稍微涉及概率统计的知识，我也很难说弄明白，但实现的时候按照算法陈述，注意映射对应关系就好。（这就叫囫囵吞枣喇）</p><p>6.伽玛变换</p><p>典型的点变换，变换公式result = c(input + Б)^&gamma; ， 其中&gamma; （gamma）是核心，它取大于1的数时能实现图像整体亮度（灰度）提升，而且原来越亮的部分会提升得更多。</p><p style="text-align: center"><a target="_self" href="http://www.zwqxin.com/archives/image-processing/image-process-point-histogram.html"><img alt="点.直方图处理 - www.zwqxin.com" src="http://lh4.ggpht.com/_lYWT-2PnV0s/Shv8yKyMR8I/AAAAAAAAAZI/2p5AAR9a6f0/s800/Snap10.jpg" /></a><br /><br/>(gamma = 1.06)</p><p>7.对数变换</p><p>也是典型得点变换，result = c*log(input&nbsp; + 1)，其实就是整体亮度降低，原来越暗部分降低得越多。基本上就是伽玛变换的对立面。</p><p style="text-align: center"><a target="_self" href="http://www.zwqxin.com/archives/image-processing/image-process-point-histogram.html"><img alt="点.直方图处理 - www.zwqxin.com" src="http://lh5.ggpht.com/_lYWT-2PnV0s/Shv8yrs_XSI/AAAAAAAAAZQ/6vXUcHyxv_w/s800/Snap12.jpg" /></a><br /><br/>(因为结果很暗，故所乘的系数取了500)</p><p>ETC呃~~再次泛泛而谈~~~~</p>]]></description><category>图像处理</category><comments>http://www.zwqxin.com/archives/image-processing/image-process-point-histogram.html#comment</comments><wfw:comment>http://www.zwqxin.com/</wfw:comment><wfw:commentRss>http://www.zwqxin.com/feed.asp?cmt=48</wfw:commentRss><trackback:ping>http://www.zwqxin.com/cmd.asp?act=tb&amp;id=48&amp;key=76a44d66</trackback:ping></item><item><title>图像处理里的空间域滤波</title><author>a@b.com (zwqxin)</author><link>http://www.zwqxin.com/archives/image-processing/image-process-spatial-domain-filter.html</link><pubDate>Mon, 27 Apr 2009 23:53:18 +0800</pubDate><guid>http://www.zwqxin.com/archives/image-processing/image-process-spatial-domain-filter.html</guid><description><![CDATA[<p>图像处理里面比较基本的操作是在空间域的滤波处理。最常见的模糊啊锐化啊的都可以归于这类。其实质就是邻域间的组合运算，在shader技术上的乒乓也就差不多这个样子，而且操作纹理要更简捷。&mdash;&mdash;<a href="http://www.zwqxin.com/">ZwqXin</a>.com</p><p>比较直方图类那种&ldquo;单点&rdquo;操作，空间域滤波中的每个像素都得多少顾及一下邻里情况，因而依其关注面大小而出现了不同大小的&ldquo;邻域&rdquo;的定义。至于不同的空间域滤波对应不同类型的矩阵式，就是所谓的&ldquo;滤波&rdquo;（FILTER），或者说模板。简单的就是3*3大小的高斯平滑，中值平滑，一阶梯度锐化/边缘，二阶拉普拉斯锐化/边缘等等，还有实现各种不同效果的FILTER，可上网找找，其实运用都是差不多的，但是要得出一个&ldquo;有价值&rdquo;的FILTER，可不是随随便便就能行的。</p><p>在[<a target="_blank" href="http://www.zwqxin.com/archives/image-processing/image-intensity-binarization.html">基于亮度的图像二值化处理</a>] 里对8BIT，16BIT，24/32BIT的BMP分别应用&ldquo;单点&rdquo;的计算，一提取BMP数据区的像素信息，马上就作转换操作了，而在&ldquo;邻域&rdquo;计算里，把全部像素信息提取出来后，再进行转换操作比较合适：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">void</span><span>&nbsp;SpatialDomainTemplate::BmpProcess24_32(OperateBMP&amp;&nbsp;image,&nbsp;</span><span class="keyword">int</span><span>&nbsp;Hdis,&nbsp;</span><span class="keyword">int</span><span>&nbsp;Wdis,&nbsp;</span><span class="keyword">int</span><span>&nbsp;step) </span></span></li>    <li><span>{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span>&nbsp;index; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span>&nbsp;Wmax&nbsp;=&nbsp;(Wdis-1)*step; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FilterColor&nbsp;*color; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FilterColor&nbsp;*result_color;&nbsp; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;FilterColor[Hdis*Wdis]; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result_color&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;FilterColor[Hdis*Wdis]; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memset(color,&nbsp;0,&nbsp;Hdis*Wdis*</span><span class="keyword">sizeof</span><span>(FilterColor)); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memset(result_color,&nbsp;0,&nbsp;Hdis*Wdis*</span><span class="keyword">sizeof</span><span>(FilterColor)); </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(</span><span class="keyword">int</span><span>&nbsp;j&nbsp;=&nbsp;0;&nbsp;j&nbsp;&lt;&nbsp;Hdis;&nbsp;j++) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(</span><span class="keyword">int</span><span>&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;Wdis;&nbsp;i++) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;index&nbsp;=&nbsp;j*Wdis&nbsp;+&nbsp;i; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color[index].blue&nbsp;&nbsp;&nbsp;=&nbsp;&nbsp;image.GetImageData().buffer[i*step&nbsp;+&nbsp;0&nbsp;+&nbsp;Wmax&nbsp;*&nbsp;j]; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color[index].green&nbsp;&nbsp;=&nbsp;&nbsp;image.GetImageData().buffer[i*step&nbsp;+&nbsp;1&nbsp;+&nbsp;Wmax&nbsp;*&nbsp;j]; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color[index].red&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;&nbsp;image.GetImageData().buffer[i*step&nbsp;+&nbsp;2&nbsp;+&nbsp;Wmax&nbsp;*&nbsp;j]; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt">&nbsp;</li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ApplyTemplate(color,&nbsp;result_color,&nbsp;Hdis,&nbsp;Wdis); </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(GetReadBack()) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(</span><span class="keyword">int</span><span>&nbsp;k&nbsp;=&nbsp;0;&nbsp;k&nbsp;&lt;&nbsp;Hdis;&nbsp;k++) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(</span><span class="keyword">int</span><span>&nbsp;l&nbsp;=&nbsp;0;&nbsp;l&nbsp;&lt;&nbsp;Wdis;&nbsp;l++) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;index&nbsp;=&nbsp;k*Wdis&nbsp;+&nbsp;l; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;image.GetImageData().buffer[l*step&nbsp;+&nbsp;0&nbsp;+&nbsp;Wmax&nbsp;*&nbsp;k]&nbsp;=&nbsp;result_color[index].blue; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;image.GetImageData().buffer[l*step&nbsp;+&nbsp;1&nbsp;+&nbsp;Wmax&nbsp;*&nbsp;k]&nbsp;=&nbsp;result_color[index].green; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;image.GetImageData().buffer[l*step&nbsp;+&nbsp;2&nbsp;+&nbsp;Wmax&nbsp;*&nbsp;k]&nbsp;=&nbsp;result_color[index].red;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;delete&nbsp;[]color; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;delete&nbsp;[]result_color; </span></li>    <li><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>void SpatialDomainTemplate::BmpProcess24_32(OperateBMP&amp; image, int Hdis, int Wdis, int step)<br/>{<br/>      int index;<br/>	  int Wmax = (Wdis-1)*step;<br/>	  FilterColor *color;<br/>	  FilterColor *result_color; <br/><br/>	         color = new FilterColor[Hdis*Wdis];<br/>	  result_color = new FilterColor[Hdis*Wdis];<br/>	  memset(color, 0, Hdis*Wdis*sizeof(FilterColor));<br/>	  memset(result_color, 0, Hdis*Wdis*sizeof(FilterColor));<br/><br/>	  for(int j = 0; j &lt; Hdis; j++)<br/>		for(int i = 0; i &lt; Wdis; i++)<br/>		{<br/>			index = j*Wdis + i;<br/>			color[index].blue   =  image.GetImageData().buffer[i*step + 0 + Wmax * j];<br/>			color[index].green  =  image.GetImageData().buffer[i*step + 1 + Wmax * j];<br/>			color[index].red    =  image.GetImageData().buffer[i*step + 2 + Wmax * j];<br/>		}<br/><br/><br/>		ApplyTemplate(color, result_color, Hdis, Wdis);<br/><br/>		if(GetReadBack())<br/>		{<br/>		  for(int k = 0; k &lt; Hdis; k++)<br/>			for(int l = 0; l &lt; Wdis; l++)<br/>			{<br/>			  index = k*Wdis + l;<br/>		      image.GetImageData().buffer[l*step + 0 + Wmax * k] = result_color[index].blue;<br/>			  image.GetImageData().buffer[l*step + 1 + Wmax * k] = result_color[index].green;<br/>			  image.GetImageData().buffer[l*step + 2 + Wmax * k] = result_color[index].red;		<br/>			}<br/><br/>		}<br/><br/>	delete []color;<br/>	delete []result_color;<br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>这是24/32BIT版本，其他BIT的可参照上篇文章，类似的。color,resultcolor数组分别作为操作函数ApplyTemplate的输入和输出，模板操作就在此函数里完成&mdash;&mdash;它无关BMP是多少BIT的，进来的是一个代表原图像的RGB数组，出来的是代表结果图像的RGB数组，就这样。当然可以看出为了结构我还是牺牲了不少&ldquo;效率&rdquo;的。</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">void</span><span>&nbsp;SpatialDomainTemplate::ApplyTemplate(FilterColor*&nbsp;oColor,&nbsp;FilterColor*&nbsp;rColor,&nbsp;</span><span class="keyword">int</span><span>&nbsp;Hdis,&nbsp;</span><span class="keyword">int</span><span>&nbsp;Wdis) </span></span></li>    <li><span>{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(FilterType&nbsp;==&nbsp;AverageBlur) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ApplyAverageBlur(oColor,&nbsp;rColor,&nbsp;Hdis,&nbsp;Wdis); </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(FilterType&nbsp;==&nbsp;MedianBlur) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ApplyMedianBlur(oColor,&nbsp;rColor,&nbsp;Hdis,&nbsp;Wdis); </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(FilterType&nbsp;==&nbsp;FirstOrderSharp) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ApplyFirstOrderSharp(oColor,&nbsp;rColor,&nbsp;Hdis,&nbsp;Wdis); </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(FilterType&nbsp;==&nbsp;SecondOrderSharp) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ApplySecondOrderSharp(oColor,&nbsp;rColor,&nbsp;Hdis,&nbsp;Wdis); </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(FilterType&nbsp;==&nbsp;SelfDefine) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ApplySelfDefine(oColor,&nbsp;rColor,&nbsp;Hdis,&nbsp;Wdis); </span></li>    <li class="alt"><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>void SpatialDomainTemplate::ApplyTemplate(FilterColor* oColor, FilterColor* rColor, int Hdis, int Wdis)<br/>{<br/>	if(FilterType == AverageBlur)<br/>		ApplyAverageBlur(oColor, rColor, Hdis, Wdis);<br/><br/>	if(FilterType == MedianBlur)<br/>		ApplyMedianBlur(oColor, rColor, Hdis, Wdis);<br/><br/>	if(FilterType == FirstOrderSharp)<br/>		ApplyFirstOrderSharp(oColor, rColor, Hdis, Wdis);<br/><br/>	if(FilterType == SecondOrderSharp)<br/>		ApplySecondOrderSharp(oColor, rColor, Hdis, Wdis);<br/><br/>	if(FilterType == SelfDefine)<br/>		ApplySelfDefine(oColor, rColor, Hdis, Wdis);<br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>根据客户端的要求，调用不同的FILTER。举最后一个（自定义）为例：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">void</span><span>&nbsp;SpatialDomainTemplate::ApplySelfDefine(FilterColor*&nbsp;oColor,&nbsp;FilterColor*&nbsp;rColor,&nbsp;</span><span class="keyword">int</span><span>&nbsp;Hdis,&nbsp;</span><span class="keyword">int</span><span>&nbsp;Wdis) </span></span></li>    <li><span>{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(</span><span class="keyword">int</span><span>&nbsp;j&nbsp;=&nbsp;0;&nbsp;j&nbsp;&lt;&nbsp;Hdis;&nbsp;j++) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(</span><span class="keyword">int</span><span>&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;Wdis;&nbsp;i++) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AdjustImageEdge(i,&nbsp;j,&nbsp;Wdis,&nbsp;Hdis); </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rColor[Index[1][1]]&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;oColor[&nbsp;Index[0][0]&nbsp;]&nbsp;*&nbsp;templateParam[2] </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+&nbsp;oColor[&nbsp;Index[0][1]&nbsp;]&nbsp;*&nbsp;templateParam[3] </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+&nbsp;oColor[&nbsp;Index[0][2]&nbsp;]&nbsp;*&nbsp;templateParam[4] </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+&nbsp;oColor[&nbsp;Index[1][0]&nbsp;]&nbsp;*&nbsp;templateParam[5] </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+&nbsp;oColor[&nbsp;Index[1][1]&nbsp;]&nbsp;*&nbsp;templateParam[6] </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+&nbsp;oColor[&nbsp;Index[1][2]&nbsp;]&nbsp;*&nbsp;templateParam[7] </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+&nbsp;oColor[&nbsp;Index[2][0]&nbsp;]&nbsp;*&nbsp;templateParam[8] </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+&nbsp;oColor[&nbsp;Index[2][1]&nbsp;]&nbsp;*&nbsp;templateParam[9] </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+&nbsp;oColor[&nbsp;Index[2][2]&nbsp;]&nbsp;*&nbsp;templateParam[10]; </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rColor[Index[1][1]]&nbsp;=&nbsp;rColor[Index[1][1]]&nbsp;*&nbsp;templateParam[0]&nbsp;/&nbsp;templateParam[1]; </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(rColor[Index[1][1]].blue&nbsp;&lt;&nbsp;0)&nbsp;rColor[Index[1][1]].blue&nbsp;=&nbsp;0; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(rColor[Index[1][1]].green&nbsp;&lt;&nbsp;0)rColor[Index[1][1]].green&nbsp;=&nbsp;0; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(rColor[Index[1][1]].red&nbsp;&lt;&nbsp;0)&nbsp;&nbsp;rColor[Index[1][1]].red&nbsp;=&nbsp;0; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt">&nbsp;</li>    <li><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>void SpatialDomainTemplate::ApplySelfDefine(FilterColor* oColor, FilterColor* rColor, int Hdis, int Wdis)<br/>{<br/>	  for(int j = 0; j &lt; Hdis; j++)<br/>		for(int i = 0; i &lt; Wdis; i++)<br/>		{<br/>            AdjustImageEdge(i, j, Wdis, Hdis);<br/><br/>	        rColor[Index[1][1]] <br/>		  = oColor[ Index[0][0] ] * templateParam[2]<br/>		  + oColor[ Index[0][1] ] * templateParam[3]<br/>		  + oColor[ Index[0][2] ] * templateParam[4]<br/>		  + oColor[ Index[1][0] ] * templateParam[5]<br/>		  + oColor[ Index[1][1] ] * templateParam[6]<br/>		  + oColor[ Index[1][2] ] * templateParam[7]<br/>		  + oColor[ Index[2][0] ] * templateParam[8]<br/>		  + oColor[ Index[2][1] ] * templateParam[9]<br/>		  + oColor[ Index[2][2] ] * templateParam[10];<br/><br/>	        rColor[Index[1][1]] = rColor[Index[1][1]] * templateParam[0] / templateParam[1];<br/><br/>			if(rColor[Index[1][1]].blue &lt; 0) rColor[Index[1][1]].blue = 0;<br/>			if(rColor[Index[1][1]].green &lt; 0)rColor[Index[1][1]].green = 0;<br/>			if(rColor[Index[1][1]].red &lt; 0)  rColor[Index[1][1]].red = 0;<br/>		}<br/><br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>对每个像素，以它为中心的3邻域每个邻接像素都乘以一个系数，相加后结果乘以一个主调节量，判断边界后得到该元素的结果。加上类型转换的截断，可以保证结果在0~255之间&mdash;&mdash;为免去计算过程越界的危险，FilerColor间是整型的运算：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">class</span><span>&nbsp;FilterColor&nbsp;{ </span></span></li>    <li><span class="keyword">public</span><span>: </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span>&nbsp;red; </span></li>    <li class="alt"><span>&nbsp;</span><span class="keyword">int</span><span>&nbsp;green; </span></li>    <li><span>&nbsp;</span><span class="keyword">int</span><span>&nbsp;blue; </span></li>    <li class="alt"><span>............ </span></li>    <li>&nbsp;</li>    <li class="alt"><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>class FilterColor {<br/>public:<br/><br/>   int red;<br/> int green;<br/> int blue;<br/>............<br/><br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>只要最后结果的类型是UCHAR（或BYTE），VC6就能自动类型转换从INT转换到UCHAR。AdjustImageEdge函数是调节边界的，这里对于边界元素，它的那些&ldquo;不存在的邻居&rdquo;都被虚拟成一个与该像素等值的像素。边界问题总是那样需要掂量啊。</p><p>高斯平滑：</p><p style="text-align: center">&nbsp;原图片：<br /><br/><a target="_self" href="http://www.zwqxin.com/archives/image-processing/image-spatial-domain-filter.html"><img alt="图像空间域-----www.zwqxin.com" src="http://lh4.ggpht.com/_lYWT-2PnV0s/Sd_-5V3LQBI/AAAAAAAAAXI/S4XICS3_y6s/s800/2.jpg" /></a><br /><br/>处理后：<br /><br/><a target="_self" href="http://www.zwqxin.com/archives/image-processing/image-spatial-domain-filter.html"><img alt="图像空间域处理-----www.zwqxin.com" src="http://lh6.ggpht.com/_lYWT-2PnV0s/Shl8IN7MXiI/AAAAAAAAAX4/BMIr0jl28mU/s800/ff.jpg" /></a></p>]]></description><category>图像处理</category><comments>http://www.zwqxin.com/archives/image-processing/image-process-spatial-domain-filter.html#comment</comments><wfw:comment>http://www.zwqxin.com/</wfw:comment><wfw:commentRss>http://www.zwqxin.com/feed.asp?cmt=47</wfw:commentRss><trackback:ping>http://www.zwqxin.com/cmd.asp?act=tb&amp;id=47&amp;key=dea81825</trackback:ping></item><item><title>基于亮度的图像二值化处理</title><author>a@b.com (zwqxin)</author><link>http://www.zwqxin.com/archives/image-processing/image-intensity-binarization.html</link><pubDate>Thu, 02 Apr 2009 02:23:53 +0800</pubDate><guid>http://www.zwqxin.com/archives/image-processing/image-intensity-binarization.html</guid><description><![CDATA[<p>图像的二值化简单来说就是把整张图片弄成只有两种颜色&mdash;&mdash;通常是黑白两色。恩，图像处理的初阶呢。&mdash;&mdash;<a href="http://www.zwqxin.com/">ZwqXin</a>.com上两篇为：<br /><br/><a target="_blank" href="http://www.zwqxin.com/archives/image-processing/bmp-operate-print-pixel.html">Bmp文件的结构与基本操作(逐像素印屏版)</a><br /><br/><a target="_blank" href="http://www.zwqxin.com/archives/image-processing/bmp-operate-copy-memory.html">认识HBITMAP与Bmp操作(整内存拷贝版)</a></p><p style="text-align: left">通常是有很多方法，针对应用吧，这里只讨论简单的一种&ldquo;战术&rdquo;。通常我处理的是彩色图片而不是灰度图片，要将整张图片的丰富颜色统统划分为黑白，关键要确定分界线&mdash;&mdash;难道是（127，127，127）？这很粗糙，应该考虑各个颜色的亮度。亮度不是RGB系统描述的，但是可以经过一段粗糙的转化。<font size="2"><span style="font-family: 宋体">一种</span><span lang="EN-US">RGB</span><span style="font-family: 宋体">颜色的亮度与各分量之间的关系：</span></font></p><p style="text-align: center"><font size="2"><span style="font-family: 宋体"><strong><font size="3">IntenSity = 0.2990 * R + 0.5870 * G<span>&nbsp; </span>+ 0.1140 * B</font></strong></span></font></p><p style="text-align: left">用上篇的方法载入BMP后，对其处理如下（24BIT，32BIT）</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">void</span><span>&nbsp;ImageBinaryzation::BmpBinary24_32(OperateBMP&amp;&nbsp;image,&nbsp;</span><span class="keyword">int</span><span>&nbsp;Hdis,&nbsp;</span><span class="keyword">int</span><span>&nbsp;Wdis,&nbsp;</span><span class="keyword">int</span><span>&nbsp;step) </span></span></li>    <li><span>{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UCHAR&nbsp;blue,&nbsp;green,&nbsp;red; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UCHAR&nbsp;result; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span>&nbsp;Hmax&nbsp;=&nbsp;(Hdis-1)*step; </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(</span><span class="keyword">int</span><span>&nbsp;j&nbsp;=&nbsp;0;&nbsp;j&nbsp;&lt;&nbsp;Hmax;&nbsp;j++) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(</span><span class="keyword">int</span><span>&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;Wdis;&nbsp;i++) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;blue&nbsp;=&nbsp;&nbsp;image.GetImageData().buffer[i*step&nbsp;+&nbsp;0&nbsp;+&nbsp;Wdis&nbsp;*&nbsp;j]; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;green&nbsp;=&nbsp;image.GetImageData().buffer[i*step&nbsp;+&nbsp;1&nbsp;+&nbsp;Wdis&nbsp;*&nbsp;j]; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;red&nbsp;=&nbsp;&nbsp;&nbsp;image.GetImageData().buffer[i*step&nbsp;+&nbsp;2&nbsp;+&nbsp;Wdis&nbsp;*&nbsp;j]; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;=&nbsp;0.2990*red+0.5870*green+0.1140*blue; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(result&gt;128) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;=&nbsp;255; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>(result&lt;=128) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;=&nbsp;0; </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;image.GetImageData().buffer[i*step&nbsp;+&nbsp;0&nbsp;+&nbsp;Wdis&nbsp;*&nbsp;j]&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;image.GetImageData().buffer[i*step&nbsp;+&nbsp;1&nbsp;+&nbsp;Wdis&nbsp;*&nbsp;j] </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;image.GetImageData().buffer[i*step&nbsp;+&nbsp;2&nbsp;+&nbsp;Wdis&nbsp;*&nbsp;j]&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;result; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt"><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>void ImageBinaryzation::BmpBinary24_32(OperateBMP&amp; image, int Hdis, int Wdis, int step)<br/>{<br/>	  UCHAR blue, green, red;<br/>	  UCHAR result;<br/>	  int Hmax = (Hdis-1)*step;<br/><br/>	  for(int j = 0; j &lt; Hmax; j++)<br/>		for(int i = 0; i &lt; Wdis; i++)<br/>		{<br/>			blue =  image.GetImageData().buffer[i*step + 0 + Wdis * j];<br/>			green = image.GetImageData().buffer[i*step + 1 + Wdis * j];<br/>			red =   image.GetImageData().buffer[i*step + 2 + Wdis * j];<br/><br/>			result = 0.2990*red+0.5870*green+0.1140*blue;<br/><br/>			if(result&gt;128)<br/>				result = 255;<br/>			else if(result&lt;=128)<br/>				result = 0;<br/><br/>		       	  image.GetImageData().buffer[i*step + 0 + Wdis * j] <br/>				= image.GetImageData().buffer[i*step + 1 + Wdis * j]<br/>				= image.GetImageData().buffer[i*step + 2 + Wdis * j] <br/>				= result;<br/><br/>		}<br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>如<a target="_blank" href="http://www.zwqxin.com/archives/image-processing/bmp-operate-print-pixel.html">之前</a>所说，逐个读图片的像素，逐个处理的手段在图像处理里是经常有的。这里step就是BMPBIT/8，对于24BIT图片是3（因为数据区每三个数据为一组，接下来移动3单位读下组），32BIT是4（注意这里没用上ALPHA分量）。通常边界问题不是那么重要.....以上操作一看就明白，最后得出的亮度两分后赋给原像素。当然如果不两分而直接给的话，就直接是张类似灰度图的真&middot;256色图了。</p><p>8BIT的版本涉及读调色板：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">void</span><span>&nbsp;ImageBinaryzation::BmpBinary8(OperateBMP&amp;&nbsp;image,&nbsp;</span><span class="keyword">int</span><span>&nbsp;Hdis,&nbsp;</span><span class="keyword">int</span><span>&nbsp;Wdis) </span></span></li>    <li><span>{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;UCHAR&nbsp;data&nbsp;=&nbsp;0; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;UCHAR&nbsp;WhiteIndex&nbsp;=&nbsp;0; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;UCHAR&nbsp;BlackIndex&nbsp;=&nbsp;0; </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;UCHAR&nbsp;blue,&nbsp;green,&nbsp;red; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;UCHAR&nbsp;result; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(</span><span class="keyword">int</span><span>&nbsp;k&nbsp;=&nbsp;0;&nbsp;k&nbsp;&lt;256;&nbsp;k++) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;red&nbsp;&nbsp;&nbsp;=&nbsp;&nbsp;&nbsp;image.GetImageData().palette[k].peRed; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;green&nbsp;=&nbsp;&nbsp;&nbsp;image.GetImageData().palette[k].peGreen; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;blue&nbsp;&nbsp;=&nbsp;&nbsp;&nbsp;image.GetImageData().palette[k].peBlue; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(red&nbsp;==&nbsp;0&nbsp;&amp;&amp;&nbsp;green&nbsp;==&nbsp;0&nbsp;&amp;&amp;&nbsp;blue&nbsp;==&nbsp;0) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BlackIndex&nbsp;=&nbsp;k; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>(red&nbsp;==&nbsp;255&nbsp;&amp;&amp;&nbsp;green&nbsp;==&nbsp;255&nbsp;&amp;&amp;&nbsp;blue&nbsp;==&nbsp;255) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WhiteIndex&nbsp;=&nbsp;k; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(</span><span class="keyword">int</span><span>&nbsp;j&nbsp;=&nbsp;0;&nbsp;j&nbsp;&lt;&nbsp;Hdis;&nbsp;j++) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(</span><span class="keyword">int</span><span>&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;Wdis;&nbsp;i++) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data&nbsp;=&nbsp;&nbsp;image.GetImageData().buffer[i&nbsp;+&nbsp;Wdis&nbsp;*&nbsp;j]; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;red&nbsp;&nbsp;&nbsp;=&nbsp;&nbsp;&nbsp;image.GetImageData().palette[data].peRed&amp;&nbsp;0xFF; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;green&nbsp;=&nbsp;&nbsp;&nbsp;image.GetImageData().palette[data].peGreen&amp;&nbsp;0xFF; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;blue&nbsp;&nbsp;=&nbsp;&nbsp;&nbsp;image.GetImageData().palette[data].peBlue&amp;&nbsp;0xFF;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;=&nbsp;0.2990*red+0.5870*green+0.1140*blue; </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(result&gt;128) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;=&nbsp;WhiteIndex;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>(result&lt;=128) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;=&nbsp;BlackIndex; </span></li>    <li class="alt">&nbsp;</li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;image.GetImageData().buffer[i&nbsp;+&nbsp;Wdis&nbsp;*&nbsp;j]&nbsp;=&nbsp;result; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HBITMAP&nbsp;temp&nbsp;=&nbsp;CreateDIBitmap(image.GetDrawDeviceHandler(), </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&amp;image.GetImageData().bitmapinfoheader,&nbsp;&nbsp;CBM_INIT,&nbsp;image.GetImageData().buffer,&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(BITMAPINFO*)&amp;image.GetImageData().bitmapinfoheader,&nbsp;DIB_RGB_COLORS); </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;image.SetBitmapHandler(temp); </span></li>    <li class="alt">&nbsp;</li>    <li><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>void ImageBinaryzation::BmpBinary8(OperateBMP&amp; image, int Hdis, int Wdis)<br/>{<br/>	UCHAR data = 0;<br/>	UCHAR WhiteIndex = 0;<br/>	UCHAR BlackIndex = 0;<br/><br/>	UCHAR blue, green, red;<br/>	UCHAR result;<br/><br/>	for(int k = 0; k &lt;256; k++)<br/>	{<br/>			 red   =   image.GetImageData().palette[k].peRed;<br/>			 green =   image.GetImageData().palette[k].peGreen;<br/>			 blue  =   image.GetImageData().palette[k].peBlue;<br/>			 <br/>			 if(red == 0 &amp;&amp; green == 0 &amp;&amp; blue == 0)<br/>				 BlackIndex = k;<br/>			 else if(red == 255 &amp;&amp; green == 255 &amp;&amp; blue == 255)<br/>				 WhiteIndex = k;<br/>	}<br/><br/>	  for(int j = 0; j &lt; Hdis; j++)<br/>		for(int i = 0; i &lt; Wdis; i++)<br/>		{<br/>			data =  image.GetImageData().buffer[i + Wdis * j];<br/>		  <br/>			 red   =   image.GetImageData().palette[data].peRed&amp; 0xFF;<br/>			 green =   image.GetImageData().palette[data].peGreen&amp; 0xFF;<br/>			 blue  =   image.GetImageData().palette[data].peBlue&amp; 0xFF;		<br/><br/>			result = 0.2990*red+0.5870*green+0.1140*blue;<br/><br/>			if(result&gt;128)<br/> 				result = WhiteIndex;             <br/>			else if(result&lt;=128)<br/>				result = BlackIndex;<br/><br/><br/>           image.GetImageData().buffer[i + Wdis * j] = result;<br/>		}<br/><br/>		HBITMAP temp = CreateDIBitmap(image.GetDrawDeviceHandler(),<br/>		   &amp;image.GetImageData().bitmapinfoheader,  CBM_INIT, image.GetImageData().buffer, <br/>		    (BITMAPINFO*)&amp;image.GetImageData().bitmapinfoheader, DIB_RGB_COLORS);<br/><br/>         image.SetBitmapHandler(temp);<br/><br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>首先是遍历调色板，找出黑色和白色对应的索引。然后遍历数据区，根据数据区里每个BIT的值调出调色板对应的值的RGB描述。接下来就跟之前一样了。但最后要刷新DDB，不然系统调色板不帮你弄的~方法是重新调用CreateDIBitmap建立DDB，并用HBITMAP描述，把该HBITMAP覆盖原来那个，关联到显示函数。</p><p>16位版本是比较麻烦的，先不说565格式的16BITBMP我还没载入显示成功，555格式的16BIT图片的像素读取也挺麻烦的。关键是设定好掩码后移位。要弄清楚的是，16BIT图片每组为2BYTE，因此读取时的步长是2。读出来的这16个BIT，前一位不管，通过掩码把它和后10位屏蔽掉，然后就是对应B分量有意义的那5位了，移位后就可以读取它了，其他分量类似（注意是*BGR顺序）。</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">void</span><span>&nbsp;ImageBinaryzation::BmpBinary16(OperateBMP&amp;&nbsp;image,&nbsp;</span><span class="keyword">int</span><span>&nbsp;Hdis,&nbsp;</span><span class="keyword">int</span><span>&nbsp;Wdis) </span></span></li>    <li><span>{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;UCHAR&nbsp;data1,data2; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;WORD&nbsp;data&nbsp;=&nbsp;0; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;UCHAR&nbsp;blue,&nbsp;green,&nbsp;red; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;UCHAR&nbsp;result; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span>&nbsp;Hmax&nbsp;=&nbsp;(Hdis-1)*2; </span></li>    <li class="alt">&nbsp;</li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(</span><span class="keyword">int</span><span>&nbsp;j&nbsp;=&nbsp;0;&nbsp;j&nbsp;&lt;&nbsp;Hmax;&nbsp;j++) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(</span><span class="keyword">int</span><span>&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;Wdis;&nbsp;i++) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data1&nbsp;=&nbsp;&nbsp;image.GetImageData().buffer[i*2&nbsp;+&nbsp;0&nbsp;+&nbsp;Wdis&nbsp;*&nbsp;j]; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data2&nbsp;=&nbsp;&nbsp;image.GetImageData().buffer[i*2&nbsp;+&nbsp;1&nbsp;+&nbsp;Wdis&nbsp;*&nbsp;j]; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data&nbsp;&nbsp;=&nbsp;&nbsp;data1&nbsp;|&nbsp;data2&lt;&lt;8;&nbsp;</span><span class="comment">//16BIT图片一次处理两字节，注意数据是低位在前高位在后 </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(image.GetImageData().bitmapinfoheader.biCompression&nbsp;==&nbsp;BI_RGB)&nbsp;</span><span class="comment">//*555格式 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;blue&nbsp;&nbsp;=&nbsp;(&nbsp;(data&amp;0x7C00)&gt;&gt;10&nbsp;)&lt;&lt;3; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;green&nbsp;=&nbsp;(&nbsp;(data&amp;0x03E0)&gt;&gt;5&nbsp;&nbsp;)&lt;&lt;3;&nbsp;&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;red&nbsp;&nbsp;&nbsp;=&nbsp;&nbsp;data&amp;0x001F&lt;&lt;3;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>(image.GetImageData().bitmapinfoheader.biCompression&nbsp;==&nbsp;BI_BITFIELDS) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;blue&nbsp;&nbsp;=&nbsp;(&nbsp;(data&amp;0xF800)&gt;&gt;11&nbsp;)&lt;&lt;3; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;green&nbsp;=&nbsp;(&nbsp;(data&amp;0x07E0)&gt;&gt;5&nbsp;&nbsp;)&lt;&lt;3;&nbsp;&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;red&nbsp;&nbsp;&nbsp;=&nbsp;&nbsp;data&amp;0x001F&lt;&lt;3;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li>&nbsp;</li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;=&nbsp;0.2990*red+0.5870*green+0.1140*blue; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(result&gt;128) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;=&nbsp;255;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>(result&lt;=128) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;=&nbsp;0; </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(image.GetImageData().bitmapinfoheader.biCompression&nbsp;==&nbsp;BI_RGB)&nbsp;</span><span class="comment">//*555格式 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data&nbsp;=&nbsp;((result&gt;&gt;3)&lt;&lt;10)&nbsp;|&nbsp;((result&gt;&gt;3)&lt;&lt;5)&nbsp;|&nbsp;(result&gt;&gt;3); </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>(image.GetImageData().bitmapinfoheader.biCompression&nbsp;==&nbsp;BI_BITFIELDS) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data&nbsp;=&nbsp;((result&gt;&gt;3)&lt;&lt;11)&nbsp;|&nbsp;((result&gt;&gt;3)&lt;&lt;5)&nbsp;|&nbsp;(result&gt;&gt;3); </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;image.GetImageData().buffer[i*2&nbsp;+&nbsp;0&nbsp;+&nbsp;Wdis&nbsp;*&nbsp;j]&nbsp;=&nbsp;LOBYTE(data); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;image.GetImageData().buffer[i*2&nbsp;+&nbsp;1&nbsp;+&nbsp;Wdis&nbsp;*&nbsp;j]&nbsp;=&nbsp;HIBYTE(data); </span></li>    <li class="alt">&nbsp;</li>    <li>&nbsp;</li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt"><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>void ImageBinaryzation::BmpBinary16(OperateBMP&amp; image, int Hdis, int Wdis)<br/>{<br/>	UCHAR data1,data2;<br/>	WORD data = 0;<br/><br/>	UCHAR blue, green, red;<br/>	UCHAR result;<br/>    int Hmax = (Hdis-1)*2;<br/><br/><br/>	  for(int j = 0; j &lt; Hmax; j++)<br/>		for(int i = 0; i &lt; Wdis; i++)<br/>		{<br/>			data1 =  image.GetImageData().buffer[i*2 + 0 + Wdis * j];<br/>			data2 =  image.GetImageData().buffer[i*2 + 1 + Wdis * j];<br/>			data  =  data1 | data2&lt;&lt;8; //16BIT图片一次处理两字节，注意数据是低位在前高位在后<br/><br/>		if(image.GetImageData().bitmapinfoheader.biCompression == BI_RGB) //*555格式<br/>		{<br/>			blue  = ( (data&amp;0x7C00)&gt;&gt;10 )&lt;&lt;3;<br/>			green = ( (data&amp;0x03E0)&gt;&gt;5  )&lt;&lt;3;   <br/>            red   =  data&amp;0x001F&lt;&lt;3;   <br/>		}<br/>		else if(image.GetImageData().bitmapinfoheader.biCompression == BI_BITFIELDS)<br/>		{<br/>			blue  = ( (data&amp;0xF800)&gt;&gt;11 )&lt;&lt;3;<br/>			green = ( (data&amp;0x07E0)&gt;&gt;5  )&lt;&lt;3;   <br/>            red   =  data&amp;0x001F&lt;&lt;3;   <br/>		}<br/><br/><br/>			result = 0.2990*red+0.5870*green+0.1140*blue;<br/><br/>			if(result&gt;128)<br/> 				result = 255;             <br/>			else if(result&lt;=128)<br/>				result = 0;<br/><br/>		if(image.GetImageData().bitmapinfoheader.biCompression == BI_RGB) //*555格式<br/>             data = ((result&gt;&gt;3)&lt;&lt;10) | ((result&gt;&gt;3)&lt;&lt;5) | (result&gt;&gt;3);<br/><br/>		else if(image.GetImageData().bitmapinfoheader.biCompression == BI_BITFIELDS)<br/>             data = ((result&gt;&gt;3)&lt;&lt;11) | ((result&gt;&gt;3)&lt;&lt;5) | (result&gt;&gt;3);<br/><br/>		      image.GetImageData().buffer[i*2 + 0 + Wdis * j] = LOBYTE(data);<br/>			  image.GetImageData().buffer[i*2 + 1 + Wdis * j] = HIBYTE(data);<br/><br/><br/><br/>		}<br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>实现出来的只有BI_RGB)&nbsp;<span class="comment">//*555格式，看看是怎样先把两个BYTE的数据合成一个WORD的（因为要在一个WORD上划分才行~），然后掩码+移位。注意移位到底端后要再向上移3位。因为对一个描述分量的UCHAR来说数据要满足占好一个BYTE（8IT）。正因为16BIT对全彩色是有损的，数据的有损（低位缺损）也是必然的咯。求出亮度，应用到颜色分量的时候，记得要复位。</span></p><p style="text-align: center">&nbsp;原图片：<br /><br/><a target="_self" href="http://www.zwqxin.com/archives/image-processing/image-intensity-binarization.html"><img alt="图像二值化-----www.zwqxin.com" src="http://lh4.ggpht.com/_lYWT-2PnV0s/Sd_-5V3LQBI/AAAAAAAAAXI/S4XICS3_y6s/s800/2.jpg" /></a><br /><br/>处理后：<br /><br/><a target="_self" href="http://www.zwqxin.com/archives/image-processing/image-intensity-binarization.html"><img alt="图像二值化-----www.zwqxin.com" src="http://lh5.ggpht.com/_lYWT-2PnV0s/Sd_-9LApoDI/AAAAAAAAAXQ/qOkeOwmcL8c/s800/22.jpg" /></a><br /><br/>其实我是真不想天空也有部分变黑的。但它在恰好亮度分界线（128）以下，可见实际应用中分界线的选取其实很重要的。</p>]]></description><category>图像处理</category><comments>http://www.zwqxin.com/archives/image-processing/image-intensity-binarization.html#comment</comments><wfw:comment>http://www.zwqxin.com/</wfw:comment><wfw:commentRss>http://www.zwqxin.com/feed.asp?cmt=45</wfw:commentRss><trackback:ping>http://www.zwqxin.com/cmd.asp?act=tb&amp;id=45&amp;key=1a585533</trackback:ping></item></channel></rss>
