﻿<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>学习日记 &#187; 取词</title>
	<atom:link href="https://www.softwareace.cn/?cat=59&#038;feed=rss2" rel="self" type="application/rss+xml" />
	<link>https://www.softwareace.cn</link>
	<description>时刻想着为自己的产品多做一些对他好的事情</description>
	<lastBuildDate>Fri, 20 Mar 2026 06:58:28 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	
	<item>
		<title>C++使用OLE高速读写EXCEL的源码</title>
		<link>https://www.softwareace.cn/?p=754</link>
		<comments>https://www.softwareace.cn/?p=754#comments</comments>
		<pubDate>Tue, 01 Apr 2014 08:22:13 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[取词]]></category>

		<guid isPermaLink="false">http://www.softwareace.cn/?p=754</guid>
		<description><![CDATA[http://www.cnblogs.com/fullsail/archive/2012/12/28/2837 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>http://www.cnblogs.com/fullsail/archive/2012/12/28/2837952.html</p>
]]></content:encoded>
			<wfw:commentRss>https://www.softwareace.cn/?feed=rss2&#038;p=754</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>获取本机内容DPI缩放比例</title>
		<link>https://www.softwareace.cn/?p=747</link>
		<comments>https://www.softwareace.cn/?p=747#comments</comments>
		<pubDate>Thu, 27 Mar 2014 06:45:34 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[取词]]></category>
		<category><![CDATA[DPI]]></category>

		<guid isPermaLink="false">http://www.softwareace.cn/?p=747</guid>
		<description><![CDATA[[crayon-69efbbb466745729596658/] &#160;]]></description>
				<content:encoded><![CDATA[<p></p><pre class="crayon-plain-tag">#define WINVER 0x0600
#include &lt;windows.h&gt;
#include &lt;iostream&gt;
#include &lt;WinUser.h&gt;
int main()
{
	{
		SetProcessDPIAware();
		HDC hdcScreen = GetDC(NULL);   //获取屏幕的HDC   
		double nScreenWidth = GetDeviceCaps(hdcScreen, LOGPIXELSX);  
		double bb = nScreenWidth/96;
		std::cout &lt;&lt;" DPI倍数"&lt;&lt; bb&lt;&lt; std::endl;
	}
}</pre><p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>https://www.softwareace.cn/?feed=rss2&#038;p=747</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>VC++获取屏幕大小第二篇 物理大小GetDeviceCaps 上 .</title>
		<link>https://www.softwareace.cn/?p=740</link>
		<comments>https://www.softwareace.cn/?p=740#comments</comments>
		<pubDate>Fri, 21 Mar 2014 08:26:14 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[取词]]></category>

		<guid isPermaLink="false">http://www.softwareace.cn/?p=740</guid>
		<description><![CDATA[上一篇《VC++获取屏幕大小第一篇像素大小GetSystemMetrics》中介绍了使用GetSystemMe [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>上一篇《<a href="http://blog.csdn.net/morewindows/article/details/8502583">VC++获取屏幕大小第一篇像素大小GetSystemMetrics</a>》中介绍了使用GetSystemMetrics函数来获取屏幕的像素大小，本篇将介绍使用GetDeviceCaps函数来获取屏幕的物理大小。下面来看看GetDeviceCaps函数的用法：</p>
<p>函数功能：用于得到被定义的系统数据或者系统配置信息</p>
<p>函数原型：获取一些设备数据</p>
<p>// By MoreWindows( http://blog.csdn.net/MoreWindows )</p>
<p align="left">int GetDeviceCaps(</p>
<p align="left">       HDChdc,     // handle to DC</p>
<p align="left">       int nIndex   // index of capability</p>
<p>);</p>
<p>参数说明：</p>
<p>第一个参数表示设备环境的HDC句柄。</p>
<p>第二个参数与GetSystemMetrics函数的参数类似，有很多种取值，这里就不一一列举了，常用的有二个：</p>
<table border="0" cellspacing="3" cellpadding="0">
<tbody>
<tr>
<td valign="top">
<p align="left">HORZSIZE</p>
</td>
<td valign="top">
<p align="left">               Width, in millimeters, of the physical screen.</p>
</td>
</tr>
<tr>
<td valign="top">
<p align="left">VERTSIZE</p>
</td>
<td valign="top">
<p align="left">               Height, in millimeters, of the physical screen.</p>
</td>
</tr>
</tbody>
</table>
<p><a href="http://blog.csdn.net/morewindows/article/details/8502592">http://blog.csdn.net/morewindows/article/details/8502592</a></p>
<p>&nbsp;</p>
<p>由GetDeviceCaps函数的介绍可知获取屏幕的物理大小非常简单，下面给出完整的源代码：</p><pre class="crayon-plain-tag">// 获取屏幕大小 物理大小 &lt;A href="http://blog.csdn.net/morewindows/article/details/8502592"&gt;http://blog.csdn.net/morewindows/article/details/8502592&lt;/A&gt;
#include &lt;stdio.h&gt;
#include &lt;windows.h&gt;
int main()
{
	printf("    获取屏幕大小 物理大小\n");        
	printf(" -- By MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");   

	int nScreenWidth, nScreenHeight;
	HDC hdcScreen = GetDC(NULL);   //获取屏幕的HDC
	nScreenWidth = GetDeviceCaps(hdcScreen, HORZSIZE);
	nScreenHeight = GetDeviceCaps(hdcScreen, VERTSIZE);

	printf("屏幕大小（毫米） 宽：%d 高：%d\n", nScreenWidth, nScreenHeight);
	return 0;
}</pre><p>&nbsp;</p>
<p>程序运行结果如下所示：</p>
<p><img alt="" src="http://img.my.csdn.net/uploads/201301/14/1358171514_7755.PNG" /></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>后面一篇《<a href="http://blog.csdn.net/morewindows/article/details/8610891">VC++获取屏幕大小第三篇物理大小GetDeviceCaps下</a>》将介绍获取屏幕的物理大小后计算屏幕对角线长度，再换算成英寸。这样可以方便大家查看自己电脑屏幕是多少英寸的，很多笔记本用户会有意外喔^_^。欢迎继续浏览。地址：<a href="http://blog.csdn.net/morewindows/article/details/8610891">http://blog.csdn.net/morewindows/article/details/8610891</a></p>
<p>&nbsp;</p>
<p>转载请标明出处，原文地址：<a href="http://blog.csdn.net/morewindows/article/details/8502592">http://blog.csdn.net/morewindows/article/details/8502592</a></p>
<p>欢迎关注微博：<a href="http://weibo.com/MoreWindows">http://weibo.com/MoreWindows</a><br />
Note:  MSDN对GetDeviceCaps函数有说明：<strong>GetDeviceCaps</strong> reports info that the display driver provides. If the display driver declines to report any info, <strong>GetDeviceCaps</strong> calculates the info based on fixed calculations. If the display driver reports invalid info, <strong>GetDeviceCaps</strong> returns the invalid info. Also, if the display driver declines to report info, <strong>GetDeviceCaps</strong> might calculate incorrect info because it assumes either fixed DPI (96 DPI) or a fixed size (depending on the info that the display driver did and didn’t provide). Unfortunately, a display driver that is implemented to the Windows Display Driver Model (WDDM) (introduced in Windows Vista) causes GDI to not get the info, so <strong>GetDeviceCaps</strong> must always calculate the info.</p>
]]></content:encoded>
			<wfw:commentRss>https://www.softwareace.cn/?feed=rss2&#038;p=740</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CDC::GetDeviceCaps()物理长度与屏幕像素间的转换</title>
		<link>https://www.softwareace.cn/?p=739</link>
		<comments>https://www.softwareace.cn/?p=739#comments</comments>
		<pubDate>Fri, 21 Mar 2014 08:24:48 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[取词]]></category>

		<guid isPermaLink="false">http://www.softwareace.cn/?p=739</guid>
		<description><![CDATA[作用: 读取DC的一些打印区域信息,主要是像素和英寸方面的数据. 声明: GetDeviceCaps(int  [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>作用:<br />
读取DC的一些打印区域信息,主要是像素和英寸方面的数据.</p>
<p>声明:<br />
GetDeviceCaps(int )</p>
<p>使用例子:<br />
//所有像素数<br />
int pagecx=dc.GetDeviceCaps(HORZRES);<br />
int pagecy=dc.GetDeviceCaps(VERTRES);</p>
<p>//即每英寸点数<br />
short cxInch = dc.GetDeviceCaps(LOGPIXELSX);<br />
short cyInch = dc.GetDeviceCaps(LOGPIXELSY);</p>
<p>// 计算一个设备单位等于多少0.1mm<br />
double scaleX = 254.0 / (double)GetDeviceCaps(dc.m_hAttribDC,LOGPIXELSX);<br />
double scaleY = 254.0 / (double)GetDeviceCaps(dc.m_hAttribDC, LOGPIXELSY);<br />
说明:<br />
主要用到的参数见例子中的:HORZRES,VERTRES,LOGPIXELSX,LOGPIXELSY.总的来说是为了方便控制打印或重画时的控制,如为了定制打印时,一般依据的是物理的长度,而不是像素,而DC一般是用像素的映射模式,所以需要一下转换,上面这个函数就为这种转换设计的.</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>GDI中有一个函数是GetDeviceCaps（），可以获取一些关于设备的一些属性，如HORZSIZE/HORZRES/LOGPIXELSX等。<br />
以上三者的关系通常满足：HORZSIZE = 25.4 * HORZRES/LOGPIXELSX<br />
HORZSIZE为屏幕水平尺寸（定为度量尺寸，以mm计），HORZRES为水平的像素总数（定为像素大小，平时所说的屏幕分辨率，但在这不这么称呼。这里，分辨率定为“每英寸的像素数”），LOGPIXELSX为逻辑像素（假设的每英寸的像素数，并不是刚才所说的实际的“分辨率”）。因此HORZSIZE也称为逻辑宽度。<br />
当我们选择“显示”属性里的大字体时，LOGPIXELSX（通常分为96dpi与120dpi）变大了，这样假设原来的字体为10磅，则原来的字体横向所占像素（实际所占的像素数）为10*（1/72）*LOGPIXELSX，现在LOGPIXELSX变大了，则字体所占像素也大了，因此看起来字体大了。如果HORZRES不变的话，则HORZSIZE应该变小。然后这是和Windows有关的，在16位OS中，HORZSIZE值是固定的。<br />
在XP系统上验证了一下，发现HORZSIZE值与LOGPIXELSX的值也是不变的，如果改变HORZRES的话，则HORZSIZE会发生相应变化，但LOGPIXELSX不变，一直是96。<br />
验证数值是：当HORZRES/VERTRES分别为800/600、1280/1024、1360/768时，LOGPIXELSX/LOGPIXELSY一直为96，但HORZSIZE/VERTSIZE分别为320/240、375/300、400/320。于是个人断定：LOGPIXELSX/LOGPIXELSY与所选的字体（如TrueType）有关，windows默认的字体LOGPIXELSX/LOGPIXELSY值是定的，选大字体或小字体取它们的值都是一样的，而一些字体是不同的。而HORZSIZE/VERTSIZE与系统版本有关，在有的系统中，这两个值是适合此分辨率的标准显示器的尺寸（定值，长宽比与分辨率的比一样），不是通过公式计算的，也不等于公式计算的值；而有的系统版本这两个值为公式所得的值。<br />
下边是petzold那本书上的两句（没摘英文的）：“</p>
<p>然而，在Windows NT中，用老的方法定义HORZSIZE和VERTSIZE值。这种方法与Windows的16位版本一致。HORZRES和VERTRES值仍然表示水平和垂直图素的数值，LOGPIXELSX和LOGPIXELSY仍然与在「控制台」的「显示器」程序中选择的字体有关。在Windows 98中，LOGPIXELSX和LOGPIXELSY的典型值是96和120 dpi，这取决于您选择的是小字体还是大字体。</p>
<p>在Windows NT中的区别是HORZSIZE和VERTSIZE值固定表示标准显示器大小。对于普通的显示卡，取得的HORZSIZE和VERTSIZE值分别是320和240毫米。这些值是相同的，与选择的图素大小无关。因此，这些值与用HORZRES、VERTRES、LOGPIXELSX和LOGPIXELSY索引从GetDeviceCaps中得到的值不同。然而，可以用前面的公式计算在Windows 98下的HORZSIZE和VERTSIZE值。</p>
<p>”</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<div>HFONT CreateFont(<br />
int nHeight,           //字体的高度<br />
int nWidth,            //字体的宽度<br />
int nEscapement,       //字体显示的角度<br />
int nOrientation,      //字体的角度<br />
int nWeight,           //字体的磅数<br />
BYTE bItalic,          //斜体字体<br />
BYTE bUnderline,       //带下划线的字体<br />
BYTE cStrikeOut,       //带删除线的字体<br />
BYTE nCharSet,         //所需的字符集<br />
BYTE nOutPrecision,    //输出的精度<br />
BYTE nClipPrecision,   //裁减的精度<br />
BYTE nQuality,         //逻辑字体与输出设备的实际<br />
//字体之间的精度<br />
BYTE nPitchAndFamily,  //字体间距和字体集<br />
LPCTSTR lpszFacename   //字体名称<br />
);</div>
<div></div>
<div>    示例:</div>
<div></div>
<div>/************************************************************************/</div>
<div>HFONT hFont;</div>
<div>HDC hDC;</div>
<div>hFont=CreateFont(10,10,0,0,FW_THIN,true,false,false,</div>
<div>CHINESEBIG5_CHARSET,OUT_CHARACTER_PRECIS,</div>
<div>CLIP_CHARACTER_PRECIS,DEFAULT_QUALITY,</div>
<div>FF_MODERN,&#8221;宋体&#8221;);</div>
<div>SelectObject(hDC,hFont);</div>
<div>/************************************************************************/</div>
]]></content:encoded>
			<wfw:commentRss>https://www.softwareace.cn/?feed=rss2&#038;p=739</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>DPI 补充</title>
		<link>https://www.softwareace.cn/?p=738</link>
		<comments>https://www.softwareace.cn/?p=738#comments</comments>
		<pubDate>Wed, 19 Mar 2014 09:37:37 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[取词]]></category>

		<guid isPermaLink="false">http://www.softwareace.cn/?p=738</guid>
		<description><![CDATA[[crayon-69efbbb46760d940079823/] 调用SetProcessDPIAware ， [&#8230;]]]></description>
				<content:encoded><![CDATA[<p></p><pre class="crayon-plain-tag">int main()
{
	while (false)
	{
		//SetProcessDPIAware();
		static int i = 0;
		i++;
		std::cout &lt;&lt; i &lt;&lt;" "&lt;&lt; "-------------------------" &lt;&lt; std::endl;
		POINT pt;
		if(GetCursorPos(&amp;pt))
		{
			std::cout &lt;&lt; pt.x &lt;&lt; ", " &lt;&lt; pt.y &lt;&lt; std::endl;
		}
		if (GetPhysicalCursorPos(&amp;pt))
		{			
			std::cout &lt;&lt; pt.x &lt;&lt; ", " &lt;&lt; pt.y &lt;&lt; std::endl;
		}
		Sleep(5000);
	}
}</pre><p>调用<a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms633543(v=vs.85).aspx">SetProcessDPIAware </a>， 我们告诉系统不要对我们的程序进行DWM虚拟化。</p>
<p>禁用DWM虚拟化 时 GetCursorPos(&amp;pt1) ， GetPhysicalCursorPos(&amp;pt2) ；   (pt1==pt2) = true</p>
]]></content:encoded>
			<wfw:commentRss>https://www.softwareace.cn/?feed=rss2&#038;p=738</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Windows 8.1 DPI 改进</title>
		<link>https://www.softwareace.cn/?p=735</link>
		<comments>https://www.softwareace.cn/?p=735#comments</comments>
		<pubDate>Mon, 17 Mar 2014 03:27:02 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[取词]]></category>

		<guid isPermaLink="false">http://www.softwareace.cn/?p=735</guid>
		<description><![CDATA[8.1 修改了一下几点： 增加了 Per-Monitor DPI Virtualization，允许不同显示器 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>8.1 修改了一下几点：</p>
<ol>
<li>增加了 Per-Monitor DPI Virtualization，允许不同显示器拥有不同的比例；引入 WM_DPICHANGED 消息通知软件「DPI 变化」。</li>
<li>可以根据显示器的信息自动识别出各显示器的密度，不用手工调了。</li>
<li>改进了跨窗口操作时座标变换，现在 LogicalToPhysicalPoint 以及 PhysicalToLogicalPoint 没必要添加了（这俩 API 也失效了，换成了 LogicalToPhysicalPointForPerMonitorDPI 和 PhysicalToLogicalPointForPerMonitorDPI）。</li>
<li>改进了在整数比时窗口放大的算法，不再使用双线过滤，而是使用最近邻近过滤来保持边缘清晰。</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>https://www.softwareace.cn/?feed=rss2&#038;p=735</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>兼谈如何写 DPI-Aware 的 windows 程序</title>
		<link>https://www.softwareace.cn/?p=729</link>
		<comments>https://www.softwareace.cn/?p=729#comments</comments>
		<pubDate>Thu, 13 Mar 2014 07:39:55 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[取词]]></category>

		<guid isPermaLink="false">http://www.softwareace.cn/?p=729</guid>
		<description><![CDATA[我在知乎上这篇关于 AlwaysMouseWheel 的文章说过，AWM 不支持高 dpi，使用有 bug。为 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>我在<a href="http://zhuanlan.zhihu.com/always-a-tool/19567835">知乎上这篇关于 AlwaysMouseWheel 的文章</a>说过，AWM 不支持高 dpi，使用有 bug。为什么我会发现呢？因为我有一个笔记本换上了 1080p 的屏幕，然后把 dpi 改成了 1.25x。Windows 从 vista 开始引入了 DPI 虚拟化技术，让 dwm 有权力直接放大窗口，而为了确保兼容性，许多 API——包括 <code>GetCursorPos</code> 和<code>WindowFromPoint</code>——都做了一些处理。</p>
<p>具体表现就是，在旧程序的窗口上方，<code>GetCursorPos</code> 的结果并非鼠标的物理座标，而是经过重新计算的新位置。如果要获取真实的鼠标座标，需要使用 Vista 开始提供的新 API：<a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa969464(v=vs.85).aspx"><code>GetPhysicalCursorPos</code></a>。<code>WindowFromPoint</code> 函数也不能用（事实上使用它会得到相当鬼畜的结果），应该使用<a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa969270(v=vs.85).aspx"><code>WindowFromPhysicalPoint</code></a>。显然 <code>AlwaysMouseWheel</code> 没有正确地使用新 api，于是它在高 dpi 下就不能正常工作了。</p>
<p>AlwaysMouseWheel 是个典型的鼠标钩子。在 Windows 下，写这种钩子程序最简单的办法是用 <a href="http://www.autohotkey.com/">AutoHotkey</a>。可能是教主光环过于强大，我在 Github 上找到了一个 <a href="https://github.com/geoffstokes/ahkfiles/blob/master/FocuslessScroll.ahk">类似的项目</a>，然后制作了一个<a href="https://gist.github.com/be5invis/6571037">正确的版本</a>。主要修改的地方有：</p>
<ul>
<li>使用 <code>GetPhysicalCursorPos</code> 获取鼠标座标，替换原来版本里的 AHK 函数</li>
<li>使用 <code>WindowFromPhysicalPoint</code> 获取指针下的窗口</li>
<li>在送滚轮消息前，将 <code>GetPhysicalCursorPos</code> 获取得到的物理座标用<a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms633536(v=vs.85).aspx"><code>PhysicalToLogicalPoint</code></a> 转换成逻辑座标，生成用于 <code>SendMessage</code> 的<code>lparam</code></li>
</ul>
<p>各位下载后，用 autohotkey 最新版运行即可。当然，它必须在 vista 及以上版本的 windows 里方可运行。</p>
<p>其实支持高 dpi，涉及到窗口间互操作的就是 Logical 和 Physical 点的变换问题了。不过我还是挺纳闷的，DPI 虚拟化这个 06 年就有的技术为什么到现在还没几个软件跟进呢？</p>
]]></content:encoded>
			<wfw:commentRss>https://www.softwareace.cn/?feed=rss2&#038;p=729</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>关于Windows高DPI的一些简单总结</title>
		<link>https://www.softwareace.cn/?p=724</link>
		<comments>https://www.softwareace.cn/?p=724#comments</comments>
		<pubDate>Tue, 11 Mar 2014 10:03:37 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[取词]]></category>

		<guid isPermaLink="false">http://www.softwareace.cn/?p=724</guid>
		<description><![CDATA[我们知道，关于高DPI的支持， Windows XP时代就开始有了, 那时关于高DPI的支持比较简单， 但是从 [&#8230;]]]></description>
				<content:encoded><![CDATA[<div>我们知道，关于高DPI的支持， Windows XP时代就开始有了, 那时关于高DPI的支持比较简单， 但是从Vista/Win7 到现在Win8 /Win8.1， Windows关于高DPI的支持已经发生了很大的变化， 下面我们依次简单介绍下。</div>
<div></div>
<div>如果说以前XP时代我们还有理由不关注高DPI，  那么在移动设备时代和大显示器的高分辨率时代， 我们就没有理由不关注高DPI了， 比如Surface Pro的分辨率是1920&#215;1080， 这种情况下如果系统我们不设置高DPI， 基本上就没法触摸和操作了，所以现在普通程序对高DPI的支持已经成为趋势了。</p>
<div>
<div></div>
<div>什么DPI？ 全称是dots per inch (DPI)， 也就是每英寸的点数，在显示器上就是每英寸的像素个数，Window上一般默认是96 dpi 作为100% 的缩放比率， 但是要注意的是该值未必是真正的显示器物理值， 只是Windows里我们的一个参考标准。</div>
<div></div>
<div>下面我们思考为什么DPI设置高了之后， 我们看到的字体会变大？ 因为系统字体是是以固定大小（宋体10号字，物理尺寸为（10/72）英寸)设计的， 当我们DPI设置高了之后 ，说明该字体要占有更多的像素， 在屏幕分辨率不变的前提下， 看起来也就大了。所以如果我们设置高DPI，通常也意味着我们的显示器是高分辨率， 里面的字体看起来太小了， 我们需要提高DPI来把内容放大。</div>
<div></div>
<div>那么我们的程序如何才能支持高DPI？ 对于高DPI的支持， 不同操作系统有不同的方案。通常来说如果我们程序支持高DPI， 意味着我们要对绘画的内容进行相应的放大， 比如字体，图片和控件等。当然， 如果我们用的是系统字体（比如GetStockObject(DEFAULT_GUI_FONT)）, 那么这种情况下我们不用操心， 因为系统会对该字体在高DPI时进行相应的放大； 如果我们是用CreateFont自己创建的字体， 那就要我们自己对该字体进行放大了。</div>
<div></div>
<div>下面我们看XP是如何对高DPI进行支持的？</div>
<div></div>
<div>XP对高DPI的支持比较差劲， 大部分情况下就是字体的放大， 当然我们程序也可以通过GetDeviceCaps(hDC, LOGPIXELSX)获取DPI后自己对绘画的内容进行缩放。</div>
<div></div>
<div><img alt="" src="http://www.cppblog.com/images/cppblog_com/weiym/xp_dpi.png" border="0" /></div>
<div></div>
<div>下面我们看Vista/Win7/Win8是如何对高DPI进行支持的？</div>
<div></div>
<div>我们知道Vista/Win7我们可以禁止DWM（Desktop Window Manager）， 该模式我们称之为Basic模式， 这种模式下的高DPI效果和XP一样。</div>
<div></div>
<div>对于DWM没有禁掉的情况， Vista/Win7/Win8 对高DPI的支持又分为2种情况， 具体看下图：</div>
<div><img alt="" src="http://www.cppblog.com/images/cppblog_com/weiym/win7_xp_dpi.png" border="0" /></div>
<div></div>
<div>一种XP风格的高DPi支持， 这种方式我们上面讨论过了；</div>
<div>还有一种是通过 DWM 虚拟化支持的 高DPI方式， 下面我们讨论下该方式:</div>
<div></div>
<div>该种方式的高DPI支持是通过DWM的缩放实现的， 具体过程是这样的， 比如我们当前系统的DPI是200%， 我们程序运行时，系统会告诉你当前DPI仍然是96（100%）， 所以我们程序会仍然按照100%的方式进行绘画， 但是但是系统给我们的坐标是根据DPI缩小过后的（也就是我们对窗口调用GetWindowRect或是通过GetSystemMetrics(SM_CXSCREEN)得到的大小会比实际大小减半） ， 当我们画完之后， DWM再对整个窗口进行200% 放大后画到屏幕上， 这样看起来我们的程序就自动支持高DPI了。</div>
<div></div>
<div> 这种方式看起来很美妙， 但是它也有缺点， 主要是经过缩放后的内容看起来会变模糊， 比如文字会有明显的锯齿。</div>
<div></div>
<div>既然DWM虚拟化用户效果有时不是那么好， 那么我们很多时候可能会自己支持高DPI， 如何让我们的程序禁用该效果？</div>
<div>事实上我们可以对每个进程对DWM虚拟化的支持进行设置和查询， 系统给我们提供了2个APi： <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms633543(v=vs.85).aspx">SetProcessDPIAware </a>和 <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa969261(v=vs.85).aspx">IsProcessDPIAware </a>， 通过调用<a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms633543(v=vs.85).aspx">SetProcessDPIAware </a>， 我们告诉系统不要对我们的程序进行DWM虚拟化。</div>
<div></div>
<div>这里还有特殊情况也提一下： 我们在高DPI下通过窗口句柄取到的坐标信息是和目标程序是否支持DWM虚拟化相关联的， 我们对其他支持DWM虚拟化的程序窗口调用GetWindowRect， 取到的坐标也是经过DWM缩放后的坐标； 对禁用DWM虚拟化程序的窗口调用GetWindowRect, 取到的坐标则是没有经过缩放的原始坐标。</div>
<div></div>
<div> 最后我们再讨论下Win8.1 对高DPI的支持, WIn8.1对高DPi以3种方式支持 <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/dn280512(v=vs.85).aspx">Process_DPI_Awareness</a> ：</div>
<div></div>
<div>
<pre class="crayon-plain-tag">typedef enum _Process_DPI_Awareness { 
  Process_DPI_Unaware&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 0,
  Process_System_DPI_Aware&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 1,
  Process_Per_Monitor_DPI_Aware&nbsp;&nbsp;= 2
} Process_DPI_Awareness;</pre><br />
<pre class="crayon-plain-tag"></pre>
</div>
<div>
<div>下面我们依次讨论这3种方式：</div>
<div></div>
<div>第一种Unaware, 该种方式是告诉系统， 我的程序不支持DPI aware, 请通过DWM虚拟化帮我们实现。 该方式和上面Win7/Win8对高DPI的支持的实现基本一样，主要区别是它通过GetWindowRect取到的坐标都是经过DWM缩放后的， 无论对方窗口是不是支持DWM虚拟化。</div>
<div></div>
<div>第二种方式是System DPI aware， 该方式下告诉系统， 我的程序会在启动的显示器上自己支持DPI aware, 所以不需要对我进行DWM 虚拟化。 但是当我的程序被拖动到其他DPI不一样的显示器时， 请对我们先进行system DWM虚拟化缩放。</div>
<div></div>
<div>第三种方式是Per Monitor DPI aware, 该方式是告诉系统， 请永远不要对我进行DWM虚拟化，我会自己针对不同的Monitor的DPi缩放比率进行缩放。</div>
<div></div>
<div>再介绍下相关API：</div>
<div><a href="http://msdn.microsoft.com/en-us/library/windows/desktop/dn302122(v=vs.85).aspx">SetProcessDpiAwareness </a>：设置当前进程对高DPi的支持方式</div>
<div><a href="http://msdn.microsoft.com/en-us/library/windows/desktop/dn302113(v=vs.85).aspx">GetProcessDpiAwareness </a>：查询某个进程对高DPI的支持方式</div>
<div><a href="http://msdn.microsoft.com/en-us/library/windows/desktop/dn280510(v=vs.85).aspx">GetDpiForMonitor </a>： 获取某个Monitor的DPI</div>
<div><a href="http://msdn.microsoft.com/en-us/library/windows/desktop/dn312083(v=vs.85).aspx">WM_DPICHANGED </a>：当某个程序窗口被拖到另外一个DPI的Monitor时收到</div>
<div></div>
<div>最后，简单总结下， 从上面我们可以看到微软在不同操作系统上对高DPI支持的改进线路，很多方面也体现了他们对老程序兼容性上的考虑， DWM虚拟化虽然很简单， 却丢失了用户体验。</div>
<div></div>
<div>PS， 我在我机器上测试发现，桌面程序基本上只有微软自己的程序能做到在高DPI下完美支持， 其他大部分程序（即使如Chrome）也是通过DWM虚拟化实现的高DPI支持。当然现在WPF和Window store App基本上都是内置支持高DPI的。</div>
<div></div>
<div>统计下， 你们的程序支持高DPI吗？</div>
<div></div>
<div>参考资料：<a href="http://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx">Writing DPI-Aware Desktop and Win32 Applications</a></div>
<div>                  <a href="http://www.kynosarges.org/WindowsDpi.html">High DPI Settings in Windows</a></div>
</div>
</div>
</div>
]]></content:encoded>
			<wfw:commentRss>https://www.softwareace.cn/?feed=rss2&#038;p=724</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>判断 本机安装 office 版本</title>
		<link>https://www.softwareace.cn/?p=701</link>
		<comments>https://www.softwareace.cn/?p=701#comments</comments>
		<pubDate>Fri, 24 Jan 2014 03:59:28 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[取词]]></category>

		<guid isPermaLink="false">http://www.softwareace.cn/?p=701</guid>
		<description><![CDATA[[crayon-69efbbb467ad3735653377/] &#160;]]></description>
				<content:encoded><![CDATA[<p></p><pre class="crayon-plain-tag">#include "msword.h"
HRESULT AutoWrap(int autoType, VARIANT *pvResult, IDispatch *pDisp, 
				 LPOLESTR ptName, int cArgs...) 
{
	// Begin variable-argument list
	va_list marker;
	va_start(marker, cArgs);

	if (!pDisp) 
	{
		_putws(L"NULL IDispatch passed to AutoWrap()");
		_exit(0);
		return E_INVALIDARG;
	}

	// Variables used
	DISPPARAMS dp = { NULL, NULL, 0, 0 };
	DISPID dispidNamed = DISPID_PROPERTYPUT;
	DISPID dispID;
	HRESULT hr;

	// Get DISPID for name passed
	hr = pDisp-&gt;GetIDsOfNames(IID_NULL, &amp;ptName, 1, LOCALE_USER_DEFAULT, &amp;dispID);
	if (FAILED(hr))
	{
		wprintf(L"IDispatch::GetIDsOfNames(\"%s\") failed w/err 0x%08lx\n", 
			ptName, hr);
		_exit(0);
		return hr;
	}

	// Allocate memory for arguments
	VARIANT *pArgs = new VARIANT[cArgs + 1];
	// Extract arguments...
	for(int i=0; i &lt; cArgs; i++) 
	{
		pArgs[i] = va_arg(marker, VARIANT);
	}

	// Build DISPPARAMS
	dp.cArgs = cArgs;
	dp.rgvarg = pArgs;

	// Handle special-case for property-puts
	if (autoType &amp; DISPATCH_PROPERTYPUT)
	{
		dp.cNamedArgs = 1;
		dp.rgdispidNamedArgs = &amp;dispidNamed;
	}

	// Make the call
	hr = pDisp-&gt;Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT,
		autoType, &amp;dp, pvResult, NULL, NULL);
	if (FAILED(hr)) 
	{
		wprintf(L"IDispatch::Invoke(\"%s\"=%08lx) failed w/err 0x%08lx\n", 
			ptName, dispID, hr);
		_exit(0);
		return hr;
	}

	// End variable-argument section
	va_end(marker);

	delete[] pArgs;

	return hr;
}

void Cwps_msword2013Dlg::OnBnClickedButton1()
{
	CString strAppName = OLESTR("word.application");
	bool bResult = false;
	IDispatch * wordApp;
	CLSID appClsid;
	if(S_OK == CLSIDFromProgID(strAppName, &amp;appClsid))
	{
		if(S_OK == CoCreateInstance(appClsid, NULL, CLSCTX_LOCAL_SERVER, IID_IDispatch, (void **)&amp;wordApp))
		{
			Word::_Application appWord;
			if(S_OK == wordApp-&gt;QueryInterface(IID_IDispatch, (void**)&amp;appWord))
			{
				CString strVersion = _T("");
				strVersion = appWord.GetVersion();
				if(_ttoi(strVersion) &gt;= 15.0)
				{
					bResult = true;
				}
			}

		}
		AutoWrap(DISPATCH_METHOD, NULL, wordApp, L"Quit", 0);
		wordApp-&gt;Release();
	}
	if(bResult)
		MessageBox(_T("ok"), _T("tip"));
	else
		MessageBox(_T("no"), _T("tip"));
	//return bResult;
}</pre><p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>https://www.softwareace.cn/?feed=rss2&#038;p=701</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>如何识别高级的验证码</title>
		<link>https://www.softwareace.cn/?p=695</link>
		<comments>https://www.softwareace.cn/?p=695#comments</comments>
		<pubDate>Wed, 22 Jan 2014 05:20:28 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[取词]]></category>
		<category><![CDATA[ocr]]></category>

		<guid isPermaLink="false">http://www.softwareace.cn/?p=695</guid>
		<description><![CDATA[一、验证码的基本知识 1. 验证码的主要目的是强制人机交互来抵御机器自动化攻击的。 2. 大部分的验证码设计者 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>一、验证码的基本知识</p>
<p>1. 验证码的主要目的是强制人机交互来抵御机器自动化攻击的。</p>
<p>2. 大部分的验证码设计者并不得要领，不了解图像处理，机器视觉，模式识别，人工智能的基本概念。</p>
<p>3. 利用验证码，可以发财，当然要犯罪：比如招商银行密码只有6位，验证码形同虚设，计算机很快就能破解一个有钱的账户，很多帐户是可以网上交易的。</p>
<p>4. 也有设计的比较好的，比如Yahoo,Google,Microsoft等。而国内Tencent的中文验证码虽然难，但算不上好。</p>
<p>二、人工智能，模式识别，机器视觉，图像处理的基本知识</p>
<p>1)主要流程：</p>
<p>比如我们要从一副图片中，识别出验证码；比如我们要从一副图片中，检测并识别出一张人脸。 大概有哪些步骤呢？</p>
<p>1.图像采集：验证码呢，就直接通过HTTP抓HTML，然后分析出图片的url，然后下载保存就可以了。 如果是人脸检测识别，一般要通过视屏采集设备，采集回来，通过A/D转操作，存为数字图片或者视频频。</p>
<p>2.预处理：检测是正确的图像格式，转换到合适的格式，压缩，剪切出ROI，去除噪音，灰度化，转换色彩空间这些。</p>
<p>3.检测：车牌检测识别系统要先找到车牌的大概位置，人脸检测系统要找出图片中所有的人脸（包括疑似人脸）；验证码识别呢，主要是找出文字所在的主要区域。</p>
<p>4.前处理：人脸检测和识别，会对人脸在识别前作一些校正，比如面内面外的旋转，扭曲等。我这里的验证码识别，“一般”要做文字的切割</p>
<p>5.训练：通过各种模式识别，机器学习算法，来挑选和训练合适数量的训练集。不是训练的样本越多越好。过学习，泛化能力差的问题可能在这里出现。这一步不是必须的，有些识别算法是不需要训练的。</p>
<p>6.识别：输入待识别的处理后的图片，转换成分类器需要的输入格式，然后通过输出的类和置信度，来判断大概可能是哪个字母。识别本质上就是分类。</p>
<p>2)关键概念：</p>
<p>图像处理：一般指针对数字图像的某种数学处理。比如投影，钝化，锐化，细化，边缘检测，二值化，压缩，各种数据变换等等。</p>
<p>1.二值化：一般图片都是彩色的，按照逼真程度，可能很多级别。为了降低计算复杂度，方便后续的处理，如果在不损失关键信息的情况下，能将图片处理成黑白两种颜色，那就最好不过了。</p>
<p>2.细化：找出图像的骨架，图像线条可能是很宽的，通过细化将宽度将为1，某些地方可能大于1。不同的细化算法，可能有不同的差异，比如是否更靠近线条中间，比如是否保持联通行等。</p>
<p>3.边缘检测：主要是理解边缘的概念。边缘实际上是图像中图像像素属性变化剧烈的地方。可能通过一个固定的门限值来判断，也可能是自适应的。门限可能是图像全局的，也可能是局部的。不能说那个就一定好，不过大部分时候，自适应的局部的门限可能要好点。被分析的，可能是颜色，也可能是灰度图像的灰度。</p>
<p>机器视觉：利用计算机来模式实现人的视觉。 比如物体检测，定位，识别。按照对图像理解的层次的差别，分高阶和低阶的理解。</p>
<p>模式识别：对事物或者现象的某种表示方式（数值，文字，我们这里主要想说的是数值），通过一些处理和分析，来描述，归类，理解，解释这些事物，现象及其某种抽象。</p>
<p>人工智能：这种概念比较宽，上面这些都属于人工智能这个大的方向。简单点不要过分学院派的理解就是，把人类的很“智能”的东西给模拟出来协助生物的人来处理问题，特别是在计算机里面。</p>
<p>三、常见的验证码的破解分析</p>
<p>以http://libcaca.zoy.org/wiki/PWNtcha这里PWNtcha项目中的资料为例分析，各种验证码的破解。（方法很多，仅仅从我个人乍看之下觉得可行的方法来分析）</p>
<p>1)Authimage</p>
<p><img alt="" src="http://huaidan.org/wp-content/uploads/img/yupoo/922715bdd0cd/h5hgdfd6.jpg" /><br />
使用的反破解技巧：</p>
<p>1.不连续的点组成字符<br />
2.有一定程度的倾斜</p>
<p>设计不好的地方：</p>
<p>1.通过纵横的直方图投影，可以找到字幕区域<br />
2.通过Hough变换，适当的参数，可以找到近似的横线，可以做倾斜矫正<br />
3.字符串的倾斜式面内的，没有太多的破解难度<br />
4.字母宽度一定，大小一定</p>
<p>2)Clubic</p>
<p><img alt="" src="http://huaidan.org/wp-content/uploads/img/yupoo/914715bdd0d3/s36cy743.jpg" /><br />
使用的反破解技巧：</p>
<p>1.字符是手写体</p>
<p>设计不好的地方：</p>
<p>1.检测切割阶段没有任何技术含量，属于设计的比较丑的<br />
2.只有数字，而且手写体变化不大<br />
3.表面看起来对识别阶段有难度，仔细分析，发现几乎不用任何高级的训练识别算法，就固定的招某些像素点是否有色彩就够了</p>
<p>3)linuxfr.org</p>
<p><img alt="" src="http://huaidan.org/wp-content/uploads/img/yupoo/029145bdd0cd/wnwz30i2.jpg" /><br />
使用的反破解技巧：</p>
<p>1.背景颜色块<br />
2.前景的横线或矩形</p>
<p>设计不好的地方：</p>
<p>1.背景色是单一色块，有形状，通过Region-Growth区域增长来很容易把背景给去掉<br />
2.前景色是标准的线条，色彩单一<br />
3.字母无粘连<br />
4.都是印刷体</p>
<p>4)Ourcolony</p>
<p><img alt="" src="http://huaidan.org/wp-content/uploads/img/yupoo/278335bdd0cf/wpdw3nuy.jpg" /><br />
使用的反破解技巧：</p>
<p>1.设计的太低级，不屑于去评价</p>
<p>设计不好的地方：</p>
<p>1.这种验证码，设计的最丑，但还是能把菜鸟搞定，毕竟学计算机的少，搞这个破解的更少，正所谓隔行如隔山</p>
<p>5)LiveJournal</p>
<p><img alt="" src="http://huaidan.org/wp-content/uploads/img/yupoo/662425bdd0d2/71jsdrn6.jpg" /><br />
使用的反破解技巧：</p>
<p>1.这个设计略微好点，使用个随机噪音，而且作为前景<br />
2.字母位置粗细都有变化</p>
<p>设计不好的地方：</p>
<p>1.字母没有粘连<br />
2.噪音类型单一<br />
3.通过在X轴的直方图投影，能准确分割字幕<br />
4.然后在Y周作直方图投影,能准确定位高度<br />
5.识别阶段，都是印刷体，简单地很</p>
<p>四、网上的一些高级验证码</p>
<p>1)ICQ</p>
<p><img alt="" src="http://huaidan.org/wp-content/uploads/img/yupoo/700595bdd0c1/s3mme4h0.jpg" /><br />
2)IMDb</p>
<p><img alt="" src="http://huaidan.org/wp-content/uploads/img/yupoo/451605bdd0c3/8qbljgnh.jpg" /><br />
3)MS MVPS</p>
<p><img alt="" src="http://huaidan.org/wp-content/uploads/img/yupoo/348055bdd0c5/7tenrvma.jpg" /></p>
<p>4)MVN Forum</p>
<p><img alt="" src="http://huaidan.org/wp-content/uploads/img/yupoo/944405bdd0c7/78o419pr.jpg" /></p>
<p>这些类型是被很多人认为比较难得类型，分析一下可以发现，字符检测，定位和分割都不是难。 唯一影响识别率的是IMDBb和MVPS这两类，字体变形略大。</p>
<p>总体来说，这些类型的破解也不难，很容易做到50%以上的识别率。</p>
<p>五、高级验证码的破解分析</p>
<p>时间关系，我简单介绍如何利用图像处理和模式识别技术，自动识别比较高级的验证码。<br />
(以风头正劲的Google为例)</p>
<p><img alt="" src="http://huaidan.org/wp-content/uploads/img/yupoo/416885bdd0c8/d31c2mon.jpg" /><br />
1)至少从目前的AI的发展程度看，没有简单的做法能自动处理各种不同的验证码，即使能力很强，那么系统自然也十分复杂强大。所以，要想在很简单的算法实现比较高级的验证码破解，必须分析不同验证码算法的特点：</p>
<p>作为一般的图像处理和计算机视觉，会考虑色彩，纹理，形状等直接的特征，同时也考虑直方图，灰度等统计特征，还考虑FFT，Wavelet等各种变换后的特征。但最终目标都是Dimension Reduction（降维）然后利于识别，不仅仅是速度的考虑。从图像的角度看，很多系统都考虑转换为灰度级甚者黑白图片。</p>
<p>Google的图片可以看出，颜色变化是虚晃一枪，不存在任何处理难度。难度是字体变形和字符粘连。</p>
<p>如果能成功的分割字符，那么后期识别无论是用SVM等分类算法，还是分析笔顺比划走向来硬识别，都相对好做。</p>
<p>2)图像处理和粘连分割</p>
<p>代码中的part1目录主要完成图像预处理和粘连字符分割<br />
001：将图像从jpg等格式转换为位图便于处理<br />
002：采用Fix/Adaptive的Threshold门限算法，将图片Bin-Value二值化。<br />
（可用003算法）<br />
003：采用OSTU分水岭算法，将图片Bin-Value二值化。<br />
（更通用，大部分时候效果更好）<br />
005：获取ROI感兴趣的区域。<br />
006：Edge Trace边缘跟踪。<br />
007：Edge Detection边界检测。<br />
008：Thin细化去骨架。<br />
009：做了一些Tidy整理。<br />
（这个一般要根据特定的Captcha算法调整）<br />
010：做切割,注意图片中红色的交叉点。<br />
011：将边缘检测和骨干交叉点监测的图像合并。<br />
（合并过程可以做分析: 比如X坐标偏移门限分析，交叉点区域纹理分析，线条走势分析，等等各种方法，找出更可能的切分点和分离后部件的组合管理。）</p>
<p><img alt="" src="http://huaidan.org/wp-content/uploads/img/yupoo/749045bdd0ca/q24gs2ea.jpg" /><br />
代码：（代码质量不高，从其他项目拷贝过来，简单修改的。）</p>
<p><a href="http://www.icylife.net/pstzine/0x02/html/pstzine_09_01.txt" target="_blank">查看代码</a>(./pstzine_09_01.txt)</p>
<p>注： 在这里，我们可以看到，基本的部件（字母是分割开了，但可以造成统一字母的被切割成多个Component。 一种做法是：利用先验知识，做分割； 另外一种做法是，和第二部分的识别结合起来。 比如按照从左至右，尝试增加component来识别，如果不能识别而且component的总宽度，总面积还比较小，继续增加。 当然不排除拒识的可能性。 ）</p>
<p>3)字符部件组合和识别。</p>
<p>part2的代码展示了切割后的字母组合，和基于svm的字符识别的训练和识别过程。Detection.cpp中展示了ImageSpam检测过程中的一些字符分割和组合，layout的分析和利用的简单技术。 而Google的验证码的识别，完全可以不用到，仅做参考。</p>
<p>SVM及使用：</p>
<p>本质上，SVM是一个分类器，原始的SVM是一个两类分类的分类器。可以通过1:1或者1:n的方式来组合成一个多类分类的分类器。 天生通过核函数的使用支持高维数据的分类。从几何意义上讲，就是找到最能表示类别特征的那些向量（支持向量SV）,然后找到一条线，能最大化分类的Margin。</p>
<p>libSVM是一个不错的实现。</p>
<p>训练间断和识别阶段的数据整理和归一化是一样的。 这里的简单做法是：</p>
<p>首先：</p>
<p>#define SVM_MAX +0.999<br />
#define SVM_MIN +0.001</p>
<p>其次：</p>
<p>扫描黑白待识别字幕图片的每个像素，如果为0(黑色，是字母上的像素),那么svm中该位置就SVM_MAX,反之则反。</p>
<p>最后：</p>
<p>训练阶段，在svm的input的前面，为该类打上标记，即是那一个字母。<br />
识别阶段，当然这个类别标记是SVM分类出来。</p>
<p>注意：</p>
<p>如果是SVM菜鸟，最好找一个在SVM外边做了包装的工具，比如样本选择，交叉验证，核函数选择这些，让程序自动选择和分析。</p>
<p>代码：通过ReginGrowth来提取单个单个的字符，然后开始识别。</p>
<p><a href="http://www.icylife.net/pstzine/0x02/html/pstzine_09_02.txt" target="_blank">查看代码</a>(./pstzine_09_02.txt)</p>
<p>六、对验证码设计的一些建议</p>
<p>1.在噪音等类型的使用上，尽力让字符和用来混淆的前景和背景不容易区分。尽力让坏人（噪音）长得和好人（字母）一样。</p>
<p>2.特别好的验证码的设计，要尽力发挥人类擅长而AI算法不擅长的。 比如粘连字符的分割和手写体（通过印刷体做特别的变形也可以）。 而不要一味的去加一些看起来比较复杂的噪音或者其他的花哨的东西。即使你做的足够复杂，但如果人也难识别，显然别人认为你是没事找抽型的。</p>
<p>3. 从专业的机器视觉的角度说，验证码的设计，一定要让破解者在识别阶段，反复在低阶视觉和高阶视觉之间多反复几次才能识别出来。 这样可以大大降低破解难度和破解的准确率。</p>
<p>七、个人郑重申明</p>
<p>1.这个问题，本身是人工智能，计算机视觉，模式识别领域的一个难题。我是虾米，菜得不能再菜的那种。作为破解者来说，是出于劣势地位。要做的很好，是很难得。总体来说，我走的是比较学院派的线路，能真正的破解难度比较高的验证码，不同于网上很多不太入流的破解方法。我能做的只有利用有限的知识，抛砖引玉而已。 很多OCR的技术，特别是离线手写体中文等文字识别的技术，个人了解有限的很，都不敢在这里乱写。</p>
<p>2.希望不要把这种技术用于非法用途。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.softwareace.cn/?feed=rss2&#038;p=695</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
