Hexo添加各种小部件

前言

  Shaun 目前还在使用对 Hexo 的主题 SPFK 自行魔改的那个主题(所谓的魔改也就是对照着 black-blue 主题修改了部分 CSS,然后又添加了一个站内搜索功能 (>^ω^<)),主题 SPFK 主体的东西其实都没改变。现在正逐渐将其完善中,遂有此文。

前言

  Shaun 目前还在使用对 Hexo 的主题 SPFK 自行魔改的那个主题(所谓的魔改也就是对照着 black-blue 主题修改了部分 CSS,然后又添加了一个站内搜索功能 (>^ω^<)),主题 SPFK 主体的东西其实都没改变。现在正逐渐将其完善中,遂有此文。

添加 QQ 邮箱联系

添加日期:2017-9-15

  进入 QQ邮箱开放平台,点击“获取邮我按钮”,登录 QQ 之后继续点击该按钮,因为 Shaun 不需要其样式,只需要其链接即可,所以就默认样式,直接点击“获取代码”即可,Shaun 默认的“HTML代码”为:

1
<a target="_blank" href="http://mail.qq.com/cgi-bin/qm_share?t=qm_mailme&email=qNvAyd3G0d3JxujOx9DFycHEhsvHxQ" style="text-decoration:none;"><img src="http://rescdn.qqmail.com/zh_CN/htmledition/images/function/qm_open/ico_mailme_01.png"/></a>

提取其中的 href,即http://mail.qq.com/cgi-bin/qm_share?t=qm_mailme&email=qNvAyd3G0d3JxujOx9DFycHEhsvHxQ,将该链接添加到 主题配置文件 中,具体如下:

1
2
subnav:
mail: "http://mail.qq.com/cgi-bin/qm_share?t=qm_mailme&email=qNvAyd3G0d3JxujOx9DFycHEhsvHxQ"

重新部署站点即可发现对应的邮箱图标,点击该图标可直接给 Shaun 发邮件。

添加 QQ 交谈链接

添加日期:2017-9-15

  进入 QQ推广,点击上方的“推广工具”,若没登录 QQ 则先登录 QQ,组件样式同样默认即可,这里需要注意的是,需要点击左边的“设置”,下滚页面,找到“安全级别设置”,如下

安全级别设置

完全公开(推荐商家,客服等用户使用,代码中显示QQ号码,易于推广)

安全加密(推荐博主,论坛用户等使用,代码中不显示QQ号码)

选中“安全加密”,不然该选项默认的为完全公开,这样 QQ 号码就直接会显示在代码中,不利于隐私保护,选中之后,点击“保存”。保存之后,再次点击“推广工具”,即可发现下方的复制代码区域的 HTML 代码已看不到明码显示的 QQ 号,(若还是能看到 QQ 号,没有任何变化,可关闭该界面,重启浏览器重新进入该界面),Shaun 的“复制这段代码并将其粘贴到您的网页上”下方区域的默认的代码为:

1
<a target="_blank" href="http://sighttp.qq.com/authd?IDKEY=b1afd83745b30922bc98e020847b86a5148d2114e62e8422"><img border="0"  src="http://wpa.qq.com/imgd?IDKEY=b1afd83745b30922bc98e020847b86a5148d2114e62e8422&pic=52" alt="点击这里给我发消息" title="点击这里给我发消息"/></a>

提取其中的 href,即http://sighttp.qq.com/authd?IDKEY=b1afd83745b30922bc98e020847b86a5148d2114e62e8422,将该链接添加到 主题配置文件 中,具体如下:

1
2
subnav:
QQ: "http://sighttp.qq.com/authd?IDKEY=4faf682653b3b7f5f47b9cb6d2bb8b81de8fa7a8fb8cee12"

重新部署站点即可发现对应的 QQ 图标,点击该图标可直接给 Shaun 发临时 QQ 消息。

添加用户访问统计信息小工具 —— RevolverMaps

添加日期:2017-10-12

  由于 Shaun 暂时不想搞 SEO,所以就没有搞站点地图,更没有将 Shaun 的站点提交到百度和 Google 的站长平台上。但 Shaun 又想查看用户访问信息(是不是很矛盾 o(╯□╰)o),而正好 Shaun 看到有个很酷炫的 3D地球 能满足 Shaun 的需求(其实很酷炫才是主要原因 O(∩_∩)O~),所以 Shaun 决定将其加入 Shaun 的站点中(当做一部分装饰品 ๑乛◡乛๑)。该插件的名称为 RevolverMaps,具体样式可以去其官网看,Shaun 就不贴图了。设置完前三步之后,第四步让用户复制代码到自己的站点上,注意第四步会让你选“new map”还是“update”,由于 Shaun 是初次使用,当然是选择默认的“new map”,如果是以前使用过,就选择“update”,并将原来使用的 script 代码输入出现的文本框并提交,这样就只是更改 3D 地球样式而不会丢失用户访问信息数据。   具体添加方法为:将复制的 script 代码放入想显示的某个 div 中。Shaun 得到的 script 代码为:

<script type="text/javascript" src="//rf.revolvermaps.com/0/0/8.js?i=50om5cdoa3h&amp;m=7&amp;c=ff0000&amp;cr1=ffffff&amp;f=arial&amp;l=49" async="async"></script>

  由于 Shaun 的博客是双栏的,Shaun 当然是把 RevolverMaps 放入左栏中,Shaun 刚开始是把得到的 script 代码放入主题文件夹下 \layout\_partial\left-col.ejs 文件末尾的

1
2
 </header>                
</div>

</header> 标签之前(即在 header 的最下端显示 RevolverMaps ),但实际用起来有点不好看;Shaun 又想干脆另外创造一个 div 放置地球,具体思路为:在 birdhouse 图标旁创建一个新的地球图标,再做一个像 birdhouse 图标一样的动画,鼠标移到地球图标时,出现一个 div,该 div 用来放置 RevolverMaps,这一步做到一半(即将一个新的地球图标并排放在 birdhouse 图标旁)发现这个效果感觉更不好看了,如果要改就需要大改了,有点麻烦 o(︶︿︶)o唉;于是 Shaun 看到鼠标放在 birdhouse 图标出现的菜单栏上,想到何不如将该菜单栏在添加一栏,创建一个 div 用来显示 RevolverMaps?事不宜迟,马上就动手添加该 div,具体添加步骤如下:

  1. 首先当然是添加一个“访问情况”的列表名称,在主题文件夹下 \layout\_partial\left-col.ejs 文件中 <ul class="tips-inner"> 下最后一个 <li> 后即 </ul> 前添加 <li>访问情况</li>

  2. 接着像其它的列表一样(点击该列表 birdhouse 图标就会改变成相应的图标),点击“访问情况”会将 birdhouse 图标改变成一个地球小图标,经查阅相应的 css 文件,其它的列表对应的图标好像是利用 div 的边框属性画出来的(某业余前端的猜测+_+),Shaun 目前还没有这样的才能,就只有投机的采用 Font Awesome 中的 globe 图标了。在主题文件夹下 \layout\_partial\left-col.ejs 文件中 <div class="icon-ctn"> 下最末尾即其对应的 </div> 前添加:

    1
    2
    3
    <div class="icon-wrap icon-globe hide" data-idx="4">
    <i class="fa fa-globe fa-spin fa-2x" aria-hidden="true"></i>
    </div>

    这样点击“访问情况”会将 birdhouse 图标变成一个旋转的地球小图标了;

  3. 接下来就需要创建“访问情况”对应的 div 了,在主题文件夹下 \layout\_partial\left-col.ejs 文件中 <div class="switch-wrap"> 下最末尾即其对应的 </div> 前添加:

    1
    2
    3
    <section class="switch-part switch-part5">
    <script type="text/javascript" src="//rf.revolvermaps.com/0/0/8.js?i=50om5cdoa3h&amp;m=7&amp;c=ff0000&amp;cr1=ffffff&amp;f=arial&amp;l=49" async="async"></script>
    </section>

    这样点击“访问情况”就能出现酷炫的 3D 地球了,才怪 :p。这样只能让 3D 地球出现在菜单界面,还需要添加修改相应的 css;

  4. 最后就是改 css 样式了,本以为这一步很简单,没想到这一步花费 Shaun 最多时间 ╮(╯_╰)╭,修改的样式位于主题文件夹下 \source\css_partial\main.styl 文件中,首先为 switch-part5 添加对应的样式,在 .switch-part4 样式后添加:

    1
    2
    3
    4
    5
    6
    .switch-part5{
    left: 400%;
    width: 100%;
    //height: 200px;
    //margin-left: 47px;
    }

    做完这一步会发现 3D 地球显示不完全,下面会缺一点,所以还需要继续修改,修改过程如下:Shaun 曾将该 width 减小(如上面代码中的注释),这样确实能让 3D 地球显示完全,但有点小,不是很好看;后面想到没显示完全可能是上层 div(.switch-area)太小且设置了 overflow: hidden;,于是这里 Shaun 首先增加了 .switch-area 的高度,这样确实能解决问题,但会使左栏的滚动条显示出来;所以 Shaun 接着尝试将 .switch-area 的 overflow: hidden; 注释掉,谁想注释掉之后出现了横向滚动条,这样更不好了,于是 Shaun 又更改为 overflow-x: hidden; ,谁想 .switch-area 又出现了竖直滚动条(感觉像拆东墙补西墙 -_-|||),查阅相关资料(CSS-overflow特性及总结)得知若 overflow-x 为 hidden,overflow-y 不为 hidden,则 overflow-y 将会自动重置为 auto,所以这里不能这样改,但 overflow: hidden; 还是得注释掉,不然上层 div 撑不开,而且不增加高度的话,还是不能完全显示 3D 地球,因为超出就隐藏了嘛;因为注释掉之后会出现横向滚动条,而又不能修改 .switch-area 的 overflow-x,所以就只能改更上层的 div,这里 Shaun 突然想起上次给左栏添加滚动条时,在 .left-col 下添加了 overflow: auto;,这次不如还修改这里,毕竟 Shaun 只想要竖直滚动条(其实不要滚动条却能滚动最好,但 Shaun 目前还没找到好的解决方案 (╯﹏╰)b),不要横向滚动条,于是将其修改为:

    1
    2
    overflow-y: auto;
    overflow-x: hidden;

    没想到这样也能解决问题,虽然还是会在左栏出现滚动条,但这样感觉比增加 .switch-area 的高度要好(嗯,应该要好吧 (~ o ~)Y)。看以后能不能改成点击“访问情况”时才出现滚动条,点击其它列表则不出现滚动条(其实把滚动条隐藏最好,但网上那个两个 div 嵌套的方法 Shaun 尝试过会出现一些奇怪的问题,等以后再试试吧 ↖((^ω^)↗)。


待续。。。

后记

  目前就添加这些小组件,以后应该会陆续添加一些其它的小东西 ↖(^ω^)↗。

参考资料

[1] 如何在自己网站上或者博客上放置QQ邮箱联系反馈http://jingyan.baidu.com/tag?tagName=%E9%82%AE%E7%AE%B1

[2] 如何在自己的博客添加QQ组件http://www.29mo.com/category/wltg

[3] 一步一步教你给自己博客添加QQ在线http://www.feizl.com/feizhuliu/QQbaodian/

OpenCV中显著性检测算法的使用

本文所用的 OpenCV 版本为 opencv-3.2.0,编程语言为 C++。

前言

  OpenCV 中实现了两种显著性检测算法,分别为 Spectral Residual 算法,出自 Xiaodi Hou and Liqing Zhang. Saliency detection: A spectral residual approach. In Computer Vision and Pattern Recognition, 2007. CVPR'07. IEEE Conference on, pages 1–8. IEEE, 2007. 和 Fine Grained Saliency 算法,出自 Sebastian Montabone and Alvaro Soto. Human detection using a mobile platform and novel features derived from a visual saliency mechanism. In Image and Vision Computing, Vol. 28 Issue 3, pages 391–402. Elsevier, 2010.。这两种算法同样是在扩展包 opencv_contrib-3.2.0 中,也是由于 opencv 官方示例程序对初学者不友好(主要是 Shaun 境界不够 o(╯□╰)o),所以 Shaun 对照其官方文档重新整理了一下。

本文所用的 OpenCV 版本为 opencv-3.2.0,编程语言为 C++。

前言

  OpenCV 中实现了两种显著性检测算法,分别为 Spectral Residual 算法,出自 Xiaodi Hou and Liqing Zhang. Saliency detection: A spectral residual approach. In Computer Vision and Pattern Recognition, 2007. CVPR'07. IEEE Conference on, pages 1–8. IEEE, 2007. 和 Fine Grained Saliency 算法,出自 Sebastian Montabone and Alvaro Soto. Human detection using a mobile platform and novel features derived from a visual saliency mechanism. In Image and Vision Computing, Vol. 28 Issue 3, pages 391–402. Elsevier, 2010.。这两种算法同样是在扩展包 opencv_contrib-3.2.0 中,也是由于 opencv 官方示例程序对初学者不友好(主要是 Shaun 境界不够 o(╯□╰)o),所以 Shaun 对照其官方文档重新整理了一下。

说明篇

  使用 OpenCV 中实现的显著性检测算法进行显著性检测十分方便简洁,利用以下三个函数就可以:

创建 Spectral Residual 算法显著性检测对象:static Ptr<StaticSaliencySpectralResidual> cv::saliency::StaticSaliencySpectralResidual::create();

Spectral Residual 算法计算显著性图:bool cv::saliency::StaticSaliencySpectralResidual::computeSaliency(InputArray image, OutputArray saliencyMap);

Fine Grained Saliency 算法显著性检测对应的函数声明同 Spectral Residual 算法类似。

计算显著性图的二值图:bool cv::saliency::StaticSaliency::computeBinaryMap(InputArray _saliencyMap, OutputArray _binaryMap) ;

具体使用方法可参考实例篇。

实例篇

  使用 OpenCV 中的显著性检测算法需要包含头文件#include <opencv2/saliency.hpp>,具体示例程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#include <opencv2/opencv.hpp>
#include <opencv2/saliency.hpp>

//******************************************************
// [opencv_contrib/modules/saliency/src/saliency.cpp](https://github.com/opencv/opencv_contrib/blob/b7dcf141507edbe544e75820c76769a7769223ac/modules/saliency/src/saliency.cpp)
//
//Ptr<Saliency> Saliency::create(const String& saliencyType)
//{
// if (saliencyType == "SPECTRAL_RESIDUAL")
// return makePtr<StaticSaliencySpectralResidual>(); //computeSaliency返回的是32FC1
// else if (saliencyType == "FINE_GRAINED")
// return makePtr<StaticSaliencyFineGrained>(); //computeSaliency返回的是8UC1
// else if (saliencyType == "BING")
// return makePtr<ObjectnessBING>();
// else if (saliencyType == "BinWangApr2014")
// return makePtr<MotionSaliencyBinWangApr2014>();
// return Ptr<Saliency>();
//}
//
// [opencv_contrib/modules/saliency/src/staticSaliency.cpp](https://github.com/opencv/opencv_contrib/blob/41b0a71ac826b1489d3e5c208ac7a95e58556caf/modules/saliency/src/staticSaliency.cpp)
//computeBinaryMap()要求输入的saliencyMap为浮点数(eg:32FC1)
//*****************************************************

void spectralResidualTest()
{
cv::Mat src_img = cv::imread("../data/true.png", CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR); // 载入最真实的原始图像
cv::namedWindow("src_img", CV_WND_PROP_ASPECTRATIO);
cv::imshow("src_img", src_img);


// [OpenCV实现显著性检测中的谱残差法(Spectral Residual Method)涉及到了傅立叶正反变换](http://blog.csdn.net/kena_m/article/details/49406687)
if (src_img.empty())
exit(-1);
if (src_img.channels() == 3)
cv::cvtColor(src_img, src_img, CV_BGR2GRAY);
cv::Mat planes[] = { cv::Mat_<float>(src_img), cv::Mat::zeros(src_img.size(), CV_32F) };
cv::Mat complex_img; //复数矩阵
merge(planes, 2, complex_img); //把单通道矩阵组合成复数形式的双通道矩阵
dft(complex_img, complex_img); // 使用离散傅立叶变换

//对复数矩阵进行处理,方法为谱残差
cv::Mat magnitude, phase_angle, mag_mean;
cv::Mat real_part, imaginary_part;
split(complex_img, planes); //分离复数到实部和虚部
real_part = planes[0]; //实部
imaginary_part = planes[1]; //虚部
cv::magnitude(real_part, imaginary_part, magnitude); //计算幅值
phase(real_part, imaginary_part, phase_angle); //计算相角

float *pre, *pim, *pm, *pp;
//对幅值进行对数化
for (int i = 0; i < magnitude.rows; i++)
{
pm = magnitude.ptr<float>(i);
for (int j = 0; j < magnitude.cols; j++)
{
*pm = log(*pm);
pm++;
}
}
blur(magnitude, mag_mean, cv::Size(5, 5)); //对数谱的均值滤波
magnitude = magnitude - mag_mean; //求取对数频谱残差
//把对数谱残差的幅值和相角划归到复数形式
for (int i = 0; i < magnitude.rows; i++)
{
pre = real_part.ptr<float>(i);
pim = imaginary_part.ptr<float>(i);
pm = magnitude.ptr<float>(i);
pp = phase_angle.ptr<float>(i);
for (int j = 0; j < magnitude.cols; j++)
{
*pm = exp(*pm);
*pre = *pm * cos(*pp);
*pim = *pm * sin(*pp);
pre++;
pim++;
pm++;
pp++;
}
}
cv::Mat planes1[] = { cv::Mat_<float>(real_part), cv::Mat_<float>(imaginary_part) };

merge(planes1, 2, complex_img); //重新整合实部和虚部组成双通道形式的复数矩阵
idft(complex_img, complex_img, cv::DFT_SCALE); // 傅立叶反变换
split(complex_img, planes); //分离复数到实部和虚部
real_part = planes[0];
imaginary_part = planes[1];
cv::magnitude(real_part, imaginary_part, magnitude); //计算幅值和相角
for (int i = 0; i < magnitude.rows; i++)
{
pm = magnitude.ptr<float>(i);
for (int j = 0; j < magnitude.cols; j++)
{
*pm = (*pm) * (*pm);
pm++;
}
}
GaussianBlur(magnitude, magnitude, cv::Size(7, 7), 2.5, 2.5);
cv::Mat invDFT, invDFTcvt;
normalize(magnitude, invDFT, 0, 255, cv::NORM_MINMAX); //归一化到[0,255]供显示
invDFT.convertTo(invDFTcvt, CV_8U); //转化成CV_8U型
cv::namedWindow("SpectualResidual", CV_WND_PROP_ASPECTRATIO);
cv::imshow("SpectualResidual", invDFTcvt);

cv::Mat thresholded;
cv::threshold(invDFTcvt, thresholded, 0, 255, CV_THRESH_OTSU);
cv::namedWindow("Thresholded Image", CV_WND_PROP_ASPECTRATIO);
cv::imshow("Thresholded Image", thresholded);

cv::Mat eroded;
// 纵向腐蚀
cv::erode(thresholded, eroded, cv::Mat(5, 1, CV_8UC1, cv::Scalar(1)), cv::Point(-1, -1), 3); // cv::Point(-1,-1)为默认参数,代表原点(描点)为矩阵中心
cv::namedWindow("eroded Image", CV_WND_PROP_ASPECTRATIO);
cv::imshow("eroded Image", eroded);

//cv::Mat thresholded;
cv::threshold(eroded, thresholded, 60, 255, CV_THRESH_BINARY);
cv::namedWindow("Thresholded eroded Image", CV_WND_PROP_ASPECTRATIO);
cv::imshow("Thresholded eroded Image", thresholded);
}

// 显著性检测算法基类
void saliencyTest()
{
cv::Mat src_img = cv::imread("../data/true.png", CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR);
cv::namedWindow("src_img", CV_WND_PROP_ASPECTRATIO);
cv::imshow("src_img", src_img);

if (src_img.empty())
exit(-1);
if (src_img.channels() == 3)
cv::cvtColor(src_img, src_img, CV_BGR2GRAY);

cv::Ptr<cv::saliency::Saliency> saliency_algorithm = cv::saliency::Saliency::create("SPECTRAL_RESIDUAL"); // FINE_GRAINED为Fine Grained Saliency算法
cv::Mat saliency_map;
if (saliency_algorithm->computeSaliency(src_img, saliency_map)) // 计算显著性图
{
cv::namedWindow("SR saliency map", CV_WND_PROP_ASPECTRATIO);
cv::imshow("SR saliency map", saliency_map);

cv::Mat saliency_map_show(saliency_map.size(), CV_8UC1);
normalize(saliency_map, saliency_map_show, 0, 255, CV_MINMAX); //归一化到[0,255]供显示
saliency_map_show.convertTo(saliency_map_show, CV_8U); //转化成CV_8U型
cv::namedWindow("saliency_map_show", CV_WND_PROP_ASPECTRATIO);
cv::imshow("saliency_map_show", saliency_map_show);

cv::Mat binary_map;
cv::saliency::StaticSaliencySpectralResidual spec;
if (spec.computeBinaryMap(saliency_map, binary_map)) // 对显著性图进行二值化
{
cv::namedWindow("binary map", CV_WND_PROP_ASPECTRATIO);
cv::imshow("binary map", binary_map);
}
}
}

// Fine Grained Saliency算法
void FGSTest()
{
cv::Mat src_img = cv::imread("../data/true.png", CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR);
cv::namedWindow("src_img", CV_WND_PROP_ASPECTRATIO);
cv::imshow("src_img", src_img);

if (src_img.empty())
exit(-1);
if (src_img.channels() == 3)
cv::cvtColor(src_img, src_img, CV_BGR2GRAY);

cv::Ptr<cv::saliency::StaticSaliencyFineGrained> fgs = cv::saliency::StaticSaliencyFineGrained::create();
cv::Mat fgs_saliency_map;
fgs->computeSaliency(src_img, fgs_saliency_map);
cv::namedWindow("FGS saliency map", CV_WND_PROP_ASPECTRATIO);
cv::imshow("FGS saliency map", fgs_saliency_map);
//cv::imwrite("../data/T_S.png", fgs_saliency_map);

cv::Mat binary_map;
cv::threshold(fgs_saliency_map, binary_map, 0, 255, CV_THRESH_OTSU);
cv::namedWindow("binary map", CV_WND_PROP_ASPECTRATIO);
cv::imshow("binary map", binary_map);
//cv::imwrite("../data/T_S_B.png", binary_map);
}

// Spectral Residual算法
void SRTest()
{
cv::Mat src_img = cv::imread("../data/true.png", CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR);
cv::namedWindow("src_img", CV_WND_PROP_ASPECTRATIO);
cv::imshow("src_img", src_img);

if (src_img.empty())
exit(-1);
if (src_img.channels() == 3)
cv::cvtColor(src_img, src_img, CV_BGR2GRAY);

cv::Ptr<cv::saliency::StaticSaliencySpectralResidual> sr = cv::saliency::StaticSaliencySpectralResidual::create();
cv::Mat sr_saliency_map;
sr->computeSaliency(src_img, sr_saliency_map);
cv::namedWindow("SR saliency map", CV_WND_PROP_ASPECTRATIO);
cv::imshow("SR saliency map", sr_saliency_map);

cv::Mat binary_map;
sr->computeBinaryMap(sr_saliency_map, binary_map);
cv::namedWindow("binary map", CV_WND_PROP_ASPECTRATIO);
cv::imshow("binary map", binary_map);
}

int main(int argc, char *argv[])
{
//spectralResidualTest();
//saliencyTest();
//FGSTest();
SRTest();

while (cv::waitKey(0) != 27) { }

return 0;
}

以上代码在 Win10 VS2013 中编译运行成功。

  这里面有个小东西需要注意,就是 computeBinaryMap() 函数,看其文档描述其中使用 K-means 算法和 Otsu 算法对显著性图进行二值化处理,其输入的显著性图数据类型应该为浮点数,OpenCV 中 Spectral Residual 算法 computeSaliency() 返回的结果为浮点数,而 Fine Grained Saliency 算法 computeSaliency() 返回的结果却是整型数据,所以这一点需要注意 Fine Grained Saliency 算法返回的结果不能直接使用 computeBinaryMap() 函数,一般对其结果直接使用 OTSU 算法进行阈值分割即可。

后记

  本文使用的这两种算法在 Shaun 的电脑上运行时间都较长,基本不可能用来处理视频流,而且在 Shaun 的这次实验中效果也不太理想,毕竟这是用来处理静态图像的两种显著性方法。不过 OpenCV 中也有用来处理视频流的显著性检测算法,其为 BING 算法,出自Ming-Ming Cheng, Ziming Zhang, Wen-Yan Lin, and Philip Torr. Bing: Binarized normed gradients for objectness estimation at 300fps. In IEEE CVPR, 2014.,实际上这是一种快速提取目标候选框的算法。

OpenCV中Selective Search算法的使用

本文所用的 OpenCV 版本为 opencv-3.2.0,编程语言为 C++。

前言

  OpenCV-3.2 中的 Selective Search 算法是在其扩展包中,所以要想使用该算法需自行编译 opencv_contrib-3.2.0。由于扩展包中的示例程序有点简陋,对初学者也不友好(Shaun 编程水平有限,粗浅评价,勿怪 (*^__^ *) 嘻嘻……),所以 Shaun 参考其官方文档及其官方示例程序写下此文。

本文所用的 OpenCV 版本为 opencv-3.2.0,编程语言为 C++。

前言

  OpenCV-3.2 中的 Selective Search 算法是在其扩展包中,所以要想使用该算法需自行编译 opencv_contrib-3.2.0。由于扩展包中的示例程序有点简陋,对初学者也不友好(Shaun 编程水平有限,粗浅评价,勿怪 (*^__^ *) 嘻嘻……),所以 Shaun 参考其官方文档及其官方示例程序写下此文。

说明篇

  该算法是选取 region proposal(一般翻译成候选区域 / 区域建议)领域中当时的 state-of-the-art。其算法具体思想出自 Jasper RR Uijlings, Koen EA van de Sande, Theo Gevers, and Arnold WM Smeulders. Selective search for object recognition. International journal of computer vision, 104(2):154–171, 2013.,若英文水平不够,还想了解其中文思想请参考文末参考资料。

  OpenCV中实现的相应函数:

void cv::ximgproc::segmentation::SelectiveSearchSegmentation::addGraphSegmentation(Ptr<GraphSegmentation> g);:添加相应的图割算法;

void cv::ximgproc::segmentation::SelectiveSearchSegmentation::addImage(InputArray img) ; :添加待处理的图片;

void cv::ximgproc::segmentation::SelectiveSearchSegmentation::addStrategy(Ptr<SelectiveSearchSegmentationStrategy> s); :添加相应的策略(颜色相似度、纹理相似度、尺寸相似度和填充相似度);

void cv::ximgproc::segmentation::SelectiveSearchSegmentation::process(std::vector<Rect> &rects);:结合图割算法和相应策略进行处理,返回候选框。

实例篇

  使用 Selective Search 算法需包含#include <opencv2/ximgproc.hpp>,完整示例程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#include <opencv2/opencv.hpp>
#include <opencv2/ximgproc.hpp>

void SSTest()
{
// [Image segmentation](http://docs.opencv.org/3.2.0/d5/df0/group__ximgproc__segmentation.html#ga5e3e721c5f16e34d3ad52b9eeb6d2860)

cv::Mat src_img = cv::imread("../data/true.png", CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR); // 载入原始图像
cv::namedWindow("src_img", CV_WINDOW_KEEPRATIO);
cv::imshow("src_img", src_img);

//// 转换为灰度图
//cv::Mat gray_img;
//cvtColor(src_img, gray_img, cv::COLOR_BGR2GRAY);

// 图割算法
cv::Ptr<cv::ximgproc::segmentation::GraphSegmentation> gs = cv::ximgproc::segmentation::createGraphSegmentation();
cv::Mat graph_segmented;
gs->processImage(src_img, graph_segmented);
normalize(graph_segmented, graph_segmented, 0, 255, CV_MINMAX); // 归一化到[0,255]供显示
graph_segmented.convertTo(graph_segmented, CV_8U); // 数据类型转化成CV_8U型
// cvtColor(graph_segmented, graph_segmented, CV_GRAY2BGR);
cv::namedWindow("graph_segmented", CV_WINDOW_KEEPRATIO);
imshow("graph_segmented", graph_segmented);

// 为selective search算法添加图割算法处理结果
cv::Ptr<cv::ximgproc::segmentation::SelectiveSearchSegmentation> ss = cv::ximgproc::segmentation::createSelectiveSearchSegmentation();
ss->addGraphSegmentation(gs);

ss->addImage(src_img); // 添加待处理的图片

// 自定义策略
cv::Ptr<cv::ximgproc::segmentation::SelectiveSearchSegmentationStrategy> sss_color = cv::ximgproc::segmentation::createSelectiveSearchSegmentationStrategyColor(); // 颜色相似度策略
cv::Ptr<cv::ximgproc::segmentation::SelectiveSearchSegmentationStrategy> sss_texture = cv::ximgproc::segmentation::createSelectiveSearchSegmentationStrategyTexture(); // 纹理相似度策略
cv::Ptr<cv::ximgproc::segmentation::SelectiveSearchSegmentationStrategy> sss_size = cv::ximgproc::segmentation::createSelectiveSearchSegmentationStrategySize(); // 尺寸相似度策略
cv::Ptr<cv::ximgproc::segmentation::SelectiveSearchSegmentationStrategy> sss_fill = cv::ximgproc::segmentation::createSelectiveSearchSegmentationStrategyFill(); // 填充相似度策略
// 添加策略
cv::Ptr<cv::ximgproc::segmentation::SelectiveSearchSegmentationStrategy> sss = cv::ximgproc::segmentation::createSelectiveSearchSegmentationStrategyMultiple(sss_color, sss_texture, sss_size, sss_fill); // 合并以上4种策略
ss->addStrategy(sss);

std::vector<cv::Rect> regions;
ss->process(regions); // 处理结果

// 显示结果
cv::Mat show_img = src_img.clone();
for (std::vector<cv::Rect>::iterator it_r = regions.begin(); it_r != regions.end(); ++it_r)
{
cv::rectangle(show_img, *it_r, cv::Scalar(0, 0, 255), 3);
}
cv::namedWindow("show_img", CV_WINDOW_KEEPRATIO);
imshow("show_img", show_img);



// -------忽略上述步骤,直接采用方便算法提取候选区域------------------------
/***************************************************************************
cv::Ptr<cv::ximgproc::segmentation::SelectiveSearchSegmentation> ss = cv::ximgproc::segmentation::createSelectiveSearchSegmentation();
ss->setBaseImage(src_img); // 采用switch* functions提取候选区域
ss->switchToSelectiveSearchFast(); // 快速提取区域

std::vector<cv::Rect> rects;
ss->process(rects);

int nb_rects = 10;

char c = (char)cv::waitKey();

while (c != 'q')
{

cv::Mat wimg = src_img.clone();

int i = 0;

for (std::vector<cv::Rect>::iterator it = rects.begin(); it != rects.end(); ++it)
{
if (i++ < nb_rects)
{
cv::rectangle(wimg, *it, cv::Scalar(0, 0, 255), 3);
}
}

cv::namedWindow("Output", CV_WINDOW_KEEPRATIO);
imshow("Output", wimg);

c = (char)cv::waitKey();

if (c == 'd')
{
nb_rects += 10;
}

if (c == 'a' && nb_rects > 10)
{
nb_rects -= 10;
}
}
********************************************************/
}


int main(int argc, char *argv[])
{
SSTest();

while (cv::waitKey(0) != 27) {}
return 0;
}

以上代码在 Win10 VS2013 中编译运行成功。

后记

  使用该算法,要想达到理想效果,一般需要调整图割算法的参数或注释中方法 switchToSelectiveSearchFast() 的参数。Shaun 的这次实验为了达到理想的选取的效果,其调整参数花了不少时间,而且该算法运行时间在 Shaun 电脑上略显长。GitHub 上也有大神自己用 opencv 实现了该算法,参考 watanika/selective-search-cpp,该算法的参数感觉比 OpenCV 自带的 Selective Search 算法要好调一些,但优化效果没有 opencv 好,其运行时间在 Shaun 电脑上更长,毕竟 OpenCV 是 Intel 的亲儿子,Intel 肯定针对处理器对 OpenCV 底层做了一定的优化。

参考资料

[1] 论文笔记:Selective Search for Object Recognitionhttp://jermmy.xyz/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/

[2] Selective Search for Object Recognition(阅读)http://blog.csdn.net/langb2014/article/category/5772811

[3] 论文笔记 《Selective Search for Object Recognition》http://blog.csdn.net/csyhhb/article/category/6048588

Win10以树形结构显示文件目录结构

前言

  本文其实可以算是标题党,Windows本身并不能以树形结构显示文件目录结构,一般需要借助第三方工具(后面去网上搜索了一下,发现 Windows 居然也有一个 tree 命令 o(╯□╰)o),Windows 虽然能用命令行显示树形结构文件目录,但不像 Linux 那样可以输入一些参数控制其输出。Win10 有个特殊的功能,就是可以使用 Ubuntu 的 bash,只需要开启这个有趣的功能,就可以将 Win10 当 Ubuntu 使用,从而像 Linux 那样只输入相关命令即可显示树形结构文件目录。

*注:值得注意的是 Win10 中的 bash 目前不支持中文输入,只能切换到英文输入才能正常输入。

前言

  本文其实可以算是标题党,Windows本身并不能以树形结构显示文件目录结构,一般需要借助第三方工具(后面去网上搜索了一下,发现 Windows 居然也有一个 tree 命令 o(╯□╰)o),Windows 虽然能用命令行显示树形结构文件目录,但不像 Linux 那样可以输入一些参数控制其输出。Win10 有个特殊的功能,就是可以使用 Ubuntu 的 bash,只需要开启这个有趣的功能,就可以将 Win10 当 Ubuntu 使用,从而像 Linux 那样只输入相关命令即可显示树形结构文件目录。

*注:值得注意的是 Win10 中的 bash 目前不支持中文输入,只能切换到英文输入才能正常输入。

准备篇

首先需要在 Win10 下开启 bash 功能。具体开启方法为:

  1. 打开 Win图标 ==》 设置 ==》 更新和安全 ==》 针对开发人员(左侧),选中开发人员模式
  2. 打开 Win图标 ==》 设置 ==》 应用 ==》 应用和功能(左侧) ==》 程序和功能(最下面的相关设置中) ==》 启用或关闭Windows功能(左侧),选中适用于Linux的Windows子系统(Beta)后点击确定。
  3. 重启计算机。打开 bash,打开 bash 的方法很多,这里列出三种:1、直接在微软小娜中输入关键字"bash"搜索 Bash on Ubuntu on Windows;2、Win键+R,输入 bash,点击确定即可打开 bash;3、Win键+R,输入 cmd,在 cmd 中输入 bash,回车即可打开 bash。打开 bash 后将会提示你是否下载安装 Ubuntu on Windows,输入 y 继续,稍等片刻即可完成下载安装。

设置篇

  安装完成后系统将会提示你设置用户名和密码。(如果这一步设置成功可以直接跳过设置篇直接看使用篇)。不知道怎的,Shaun 这一步没有完成,每次系统都是直接以 root 用户登录,而且没有密码,为了安全考虑,也幸好登录时是 root 用户,可以自由对系统修改。所以 Shaun 需要对 root 密码进行修改,并创建新的用户。具体过程需执行以下命令:

root 用户下,修改用户密码:

1
passwd 用户名 (修改密码)

  由于 Shaun 需要修改 root 密码,所以该用户名即为 root,执行之后需要输入新密码(在 *nix 哲学中,密码是不会显示在输入屏幕中的,所以如果在输入密码时发现屏幕没有任何变化是没关系的,只管输入即可 ↖(^ω^)↗),两次输入完成后会显示密码更新成功。

接下来需要创建新的普通用户,在 root 用户下执行:

1
adduser xxx # 这样的命令会在home目录下添加一个帐号

或者

1
useradd xxx #仅仅是添加用户,不会在home目录添加帐号

  推荐使用前者,这样可以很明确已经成功创建新用户,而且如果用户需要存放一些文件也更安全和方便。

  在 *nix 中,绝对不推荐直接使用root用户对系统执行各种命令,毕竟其权限太大,一旦误操作将造成无法挽回的后果。有些命令普通用户可能没有权限执行,这时需要提高其权限,普通用户临时获取 root 权限的方法为:在需要执行的命令前添加sudo,像上文中如果普通用户需要创建新用户 xxx 则需要执行sudo adduser xxx,执行以上命令后同样需要输入新用户的密码。

使用篇

  先切换至普通用户,执行su xxx切换用户,即可发现 shell 提示符由#变为$,前面的用户名由root变为xxx;执行cd ~切换至用户目录。由于 Ubuntu 系统中本身没有 tree 这个命令,需要执行以下命令安装 tree 命令工具:

1
sudo apt install tree

  直接输入tree命令,系统将会自动以树形结构列出当前目录中所有文件及文件夹;执行tree -L N 命令,以树形结构查看当前 N 级的目录和文件,eg:以树形结构查看当前目录二级文件结构,则执行tree -L 2。若想将输出的2级文件结构保存至上一层文件的tree.txt文件中,可执行tree -L 2 > ../tree.txt,进入上一层目录cd ..,打开 tree.txt 即可发现该目录的文件结构。

后记

  遇事还是需要多查证一下啊,想当然果然是会出问题的,文章开头差点就犯错误了 ~\(≧▽≦)/~。本文其实是在写 Win10+VS2013+CMake-gui编译和配置OpenCV-3.2.0 时,为了方便显示输出文件结构而查找的相关资料。

参考资料

[1]linux tree命令以树形结构显示文件目录结构http://jingyan.baidu.com/tag?tagName=linux

[2] win tree命令 tree导出目录 tree显示树形结构http://jingyan.baidu.com/tag?tagName=%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F

[3] win10下linux系统的安装(开启)和使用

[4] Ubuntu建立和删除用户

[5] linux修改root密码和linux忘记root密码后找回密码的方法

Hexo添加站内本地搜索

前言

  虽然 Shaun 博客目前数量不多,质量也不高,但抱着搞事的心态,先弄它一个站内本地搜索再说。

前言

  虽然 Shaun 博客目前数量不多,质量也不高,但抱着搞事的心态,先弄它一个站内本地搜索再说。

准备篇

  要想使用本地搜索功能,首先需要安装相应的搜索插件 hexo-generator-searchdb,网上可能大多数用的是 hexo-generator-search 这个插件,也有都装的,但 Shaun 就只安装这一个了,好像 hexo-generator-searchdb 更完善一点,由于 Shaun 前端接触的极少,所以就没有一一对比了,网上也没查到具体对比情况,有兴趣的童靴可以试试 (╯▽╰)。至于具体安装如下,在站点根目录执行:

1
npm install hexo-generator-searchdb --save

  安装完之后重新生成页面,将会发现 public文件夹 下多出一个 search.xml 文件。然后在配置文件 _config.yml 中添加:

1
2
3
4
# 站点本地搜索
search:
path: search.xml
field: all

其中:

  • path - 指定生成的索引数据的文件名。默认为 search.xml 。
  • field - 指定索引数据的生成范围。可选值包括:
    • post - 只生成博客文章(post)的索引(默认);
    • page - 只生成其他页面(page)的索引;
    • all - 生成所有文章和页面的索引。

  至于是在 主题配置文件,还是在 站点配置文件 中添加,个人觉得都没关系,附:Shaun 是在主题配置文件中添加的。

接下来就需要修改原主题的代码了。

改码篇

  由于 Shaun 博客主题是基于 SPFK 对照着 black-blue 进行修改的,而且因为 black-blue 是有搜索的(Shaun 不知道 black-blue 主题的作者是如何完成的,借助了什么技术),所以 Shaun 就看 black-blue 的搜索功能是修改了 SPFK 哪个地方,再将相应的代码添加至 SPFK 中(其中相应的代码来自让 Hexo 博客支持本地站内搜索),从而逐渐完成本次搜索功能。

首先找到 spfk 主题下的 left-col.ejs 文件,对其修改如下:

1
2
3
4
5
6
7
8
9
10
11
12
<% if (theme.search_box){ %>
<!-- <form>
<input type="text" class="st-default-search-input search" id="search" placeholder=" Search...">
</form> -->

<form id="search-form"> <!-- 搜索框相关 -->
<input type="text" id="local-search-input" name="q" results="0" placeholder="Search..." class="search form-control" autocomplete="off" autocorrect="off"/>
<i class="fa fa-times" onclick="resetSearch()"></i> <!-- 清空/重置搜索框 -->
</form>
<div id="local-search-result"></div> <!-- 搜索结果区 -->
<p class='no-result'>No results found </p> <!-- 无匹配时显示,注意请在 CSS 中设置默认隐藏 -->
<%}%>

其次找到 spfk 主题下的 after-footer.ejs 文件,将其修改如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
<% if (theme.search_box){ %>
<!-- <script type="text/javascript">
window.onload = function(){
document.getElementById("search").onclick = function(){
console.log("search")
search();
}
}
function search(){
(function(w,d,t,u,n,s,e){w['SwiftypeObject']=n;w[n]=w[n]||function(){
(w[n].q=w[n].q||[]).push(arguments);};s=d.createElement(t);
e=d.getElementsByTagName(t)[0];s.async=1;s.src=u;e.parentNode.insertBefore(s,e);
})(window,document,'script','//s.swiftypecdn.com/install/v2/st.js','_st');

_st('install','A1Pz-LKMXbrzcFg2FWi6','2.0.0');
}
</script> -->

<script type="text/javascript">
// 激活搜索框时才搜索
var inputArea = document.querySelector("#local-search-input");
var getSearchFile = function(){
// 调用搜索函数
var search_path = "<%- config.search.path %>";
if (search_path.length == 0) {
search_path = "search.xml";
}
var path = "<%- config.root %>" + search_path;
searchFunc(path, 'local-search-input', 'local-search-result');
}
inputArea.onfocus = function(){ getSearchFile() }

// 搜索重置
var $resetButton = $("#search-form .fa-times");
var $resultArea = $("#local-search-result");
inputArea.oninput = function(){ $resetButton.show(); }
resetSearch = function(){
$resultArea.html("");
document.querySelector("#search-form").reset();
$resetButton.hide();
$(".no-result").hide();
}

// 屏蔽回车
inputArea.onkeydown = function(){ if(event.keyCode==13) return false}

// 无搜索结果
$resultArea.bind("DOMNodeRemoved DOMNodeInserted", function(e) {
if (!$(e.target).text()) {
$(".no-result").show(200);
} else {
$(".no-result").hide();
}
})

// 搜索函数
var searchFunc = function(path, search_id, content_id) {
'use strict';
$.ajax({
url: path,
dataType: "xml",
success: function( xmlResponse ) {
// get the contents from search data
var datas = $( "entry", xmlResponse ).map(function() {
return {
title: $( "title", this ).text(),
content: $("content",this).text(),
url: $( "url" , this).text()
};
}).get();
var $input = document.getElementById(search_id);
var $resultContent = document.getElementById(content_id);
$input.addEventListener('input', function(){
var str='<ul class=\"search-result-list\">';
var keywords = this.value.trim().toLowerCase().split(/[\s\-]+/);
$resultContent.innerHTML = "";
if (this.value.trim().length <= 0) {
return;
}
// perform local searching
datas.forEach(function(data) {
var isMatch = true;
var content_index = [];
var data_title = data.title.trim().toLowerCase();
var data_content = data.content.trim().replace(/<[^>]+>/g,"").toLowerCase();
var data_url = data.url;
var index_title = -1;
var index_content = -1;
var first_occur = -1;
// only match artiles with not empty titles and contents
if(data_title != '' && data_content != '') {
keywords.forEach(function(keyword, i) {
index_title = data_title.indexOf(keyword);
index_content = data_content.indexOf(keyword);
if( index_title < 0 && index_content < 0 ){
isMatch = false;
} else {
if (index_content < 0) {
index_content = 0;
}
if (i == 0) {
first_occur = index_content;
}
}
});
}
// show search results
if (isMatch) {
str += "<li><a href='/"+ data_url +"' class='search-result-title' target='_blank'>"+ "> " + data_title +"</a>";
var content = data.content.trim().replace(/<[^>]+>/g,"");
if (first_occur >= 0) {
// cut out characters
var start = first_occur - 6;
var end = first_occur + 6;
if(start < 0){
start = 0;
}
if(start == 0){
end = 10;
}
if(end > content.length){
end = content.length;
}
var match_content = content.substr(start, end);
// highlight all keywords
keywords.forEach(function(keyword){
var regS = new RegExp(keyword, "gi");
match_content = match_content.replace(regS, "<em class=\"search-keyword\">"+keyword+"</em>");
})
str += "<p class=\"search-result\">" + match_content +"...</p>"
}
}
})
$resultContent.innerHTML = str;
})
}
})
}
</script>
<%}%>

最后找到 spfk 主题下的 main.styl 文件,在其末尾添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/*搜索框*/
.search {
width: 68%;
height: 18px;
margin-top: 1px;
padding: 0;
font-family: inherit;
border: 2px solid transparent;
border-bottom: 2px solid #d3d3d3;
border-radius: 2px;
opacity: 0.65;
background: none;
}
.search:hover {
border: 2px solid #d3d3d3;
opacity: 1;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
}

/*搜索重置按钮*/
#search-form .fa-times {
display: none;
padding: 1px 0.7em;
box-shadow: 0 0 3px rgba(0,0,0,0.15);
cursor: pointer;
color: #4094c7;
}
#search-form .fa-times:active {
background: #d3d3d3;
}
#search-form .fa-times:hover {
zoom: 1.1;
padding: 1px 0.6em;
border: 1px solid #d3d3d3;
box-shadow: 0 0 6px rgba(0,0,0,0.25);
}

/*搜索结果区*/
#local-search-result {
//margin: auto -12% auto -6%;
margin: 0;
font-size: 0.9em;
text-align: left;
word-break: break-all;

box-shadow: 4px 4px 6px rgba(0,0,0,0.46);
}

#local-search-result ul.search-result-list li:hover {
font-weight: normal;
}

/*单条搜索结果*/
#local-search-result li {
margin: 0.5em auto;
border-bottom: 2px solid #d3d3d3;
}
#local-search-result .search-result-list li:hover {
background: rgba(47,46,46,0.8);
box-shadow: 0 0 5px rgba(0,0,0,0.2);
}

/*匹配的标题*/
#local-search-result a.search-result-title {
line-height: 1.2;
font-weight: bold;
color: #4094c7;
}

/*搜索预览段落*/
#local-search-result p.search-result {
margin: 0.4em auto;
line-height: 1.2em;
max-height: 3.6em;
overflow: hidden;
font-size: 0.8em;
text-align: justify;
color: #ffffffb3;
}

/*匹配的关键词*/
#local-search-result em.search-keyword {
color: #f58e90;
border-bottom: 1px dashed #f58e90;
font-weight: bold;
font-size: 1em;
}

/*无匹配搜索结果时显示*/
p.no-result {
display: none;
margin: 2em 0 2em 6%;
padding-bottom: 0.5em;
text-align: left;
color: #808080;
font-family: font-serif serif;
border-bottom: 2px solid #d3d3d3;
}

  这里请注意,当对 main.styl 文件做以上修改时,可能会发现有两个 .search 样式,而且相差不大,这时,不要对其原有的 .search 进行修改,更不要去注释掉它,只做上述修改就不用管了,不然可能会发生一些奇怪的事 o(>﹏<)o。Shaun 当时做以上修改时,将其原有的 .search 样式注释掉之后,整个页面的 css 布局全部都乱了 (╯﹏╰),不知道为什么 (⊙_⊙?),这两个同名样式看起来明明差不多的,最后只能维持现状了,等以后有机会再看看吧,业余前端伤不起啊! ╮(╯_╰)╭。

  至此整个站内本地搜索功能基本完成,勉强可以使用站内搜索功能了。

问题篇

  *注:以下问题于 2018-03-02 都已经解决 ╮(╯▽╰)╭。

1、搜索函数返回的 url 地址有问题。

  问题描述:当点击搜索结果时,新弹出的标签页地址栏中 url 地址会有部分乱码情况;当鼠标移到搜索的结果列表上时,浏览器左下角显示的 url 地址虽然没有乱码情况,但其中有一个重复的/符号。所幸这两个问题并没有造成浏览器解析错误,浏览器还是可以正常显示页面的。

================= 修改日期:2018-03-02 =================

  解决办法:\blog\node_modules\hexo-generator-searchdb\templates\xml.ejs 文件中的 <url><%- encodeURIComponent(config.root + post.path) %></url> 修改为 <url><%- encodeURI(post.path) %></url> ,使其中一些 url 中常见的字符(如:&, ?, /, =)不被十六进制的转义序列进行替换。

参考:escape,encodeURI,encodeURIComponent有什么区别?JavaScript encodeURIComponent() 函数

=====================================================

2、搜索结果区布局有问题。

  问题描述:当显示搜索结果时,搜索结果区会上下扩张,从而将其上下本来存在的一些布局挤开,造成布局混乱。这其实不算是一个 spfk 主题或者新添加的搜索功能的问题,而是新添加的一个东西又没有相应的和原本布局结合的布局文件,那就极大可能会有布局混乱的问题,至于这个要和原本布局契合的搜索结果区布局文件就只有等 Shaun 以后有机会有时间再完善去喽 ╮(╯▽╰)╭。

3、搜索框激活问题。

  问题描述:搜索框激活延迟很大,有时过很久或者需要切换站内页面它才能激活,给人的感觉就是好像没有搜索功能似的。添加搜索框激活功能据作者 MOxFIVE 所说是为了不让索引文件影响页面加载速度,MOxFIVE 同时也在文末指出了一些不足之处,如果索引文件太大,可能还是会造成一些问题,但 Shaun 的博客数量又不多,所以估计还是 Shaun 的代码混合问题,而且 MOxFIVE 的博客搜索功能好像没这个问题(至少 Shaun 目前没发现)。这个问题同样只有等以后再说了 (*^__^*) 嘻嘻……。

后记

  本文添加的本地搜索还很粗糙,还有很多地方需要以后去完善。但这好歹是一个好的开始,搜索功能至少勉强能够正常使用,总比以前是个空壳要好,以后有机会再慢慢去去完善吧 ↖(^ω^)↗。

参考资料

[1] jQuery-based Local Search Engine for Hexohttp://www.hahack.com/categories/codes/

[2] 让 Hexo 博客支持本地站内搜索http://moxfive.xyz/tags/Hexo/

[3] Hexo博客添加站内搜索https://www.ezlippi.com/categories/hexo/

[4] Hexo本地搜索及部分SEO优化https://www.oyohyee.com/categories/Note/

用OpenCV显示OpenGL图形

前言

  本文就是一个小实验,试验 OpenCV 到底能不能支持 OpenGL 图形显示。

前言

  本文就是一个小实验,试验 OpenCV 到底能不能支持 OpenGL 图形显示。

正文

  如果在 OpenCV 用 CMake 编译时勾选 WITH_OPENGL 且编译一切顺利的话,编译和配置的具体步骤和情况可以看 Shaun 写的一篇文档:Win10+VS2013+CMake-gui编译和配置OpenCV-3.2.0 ,那么就可以用 OpenCV 窗口显示 OpenGL 图形。

  在 VS 下使用 Windows 原有的 OpenGL 函数需要包含以下头文件和库文件:

1
2
3
4
5
6
#include <Windows.h>
#include <GL/gl.h>
#include <GL/glu.h>

#pragma comment(lib, "OpenGL32.lib")
#pragma comment(lib, "glu32.lib")

  在 OpenCV 中显示 OpenGL 图形需要 cv::namedWindow(openGLWindowName, cv::WINDOW_OPENGL),在 namedWindow 函数中添加 cv::WINDOW_OPENGL 参数说明该窗口支持 OpenGL 图形。

附示例程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
#include <opencv.hpp>

#include <Windows.h>
#include <GL/gl.h>
#include <GL/glu.h>

#pragma comment(lib, "OpenGL32.lib")
#pragma comment(lib, "glu32.lib")

static const float vertex_list[][3] =
{
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
};

// 将要使用的顶点的序号保存到一个数组里面

static const GLint index_list[][2] =
{
{ 0, 1 },
{ 2, 3 },
{ 4, 5 },
{ 6, 7 },
{ 0, 2 },
{ 1, 3 },
{ 4, 6 },
{ 5, 7 },
{ 0, 4 },
{ 1, 5 },
{ 7, 3 },
{ 2, 6 }
};

static float rotate = 0;
static int times = 0;

GLint windowWidth = 800;
GLint windowHeight = 800;

GLfloat xRotAngle = -75.0f;
GLfloat yRotAngle = 0.0f;
GLfloat zRotAngle = -135.0f;

float MIN_X = -200;
float MAX_X = 200;

float MIN_Y = -200;
float MAX_Y = 200;

float MIN_Z = -200;
float MAX_Z = 200;

GLfloat coordinatesize = 200.0f;
GLfloat ratio = 1;

void drawLine(float x1, float y1, float z1, float x2, float y2, float z2)
{
glBegin(GL_LINES);
glVertex3f(x1, y1, z1);
glVertex3f(x2, y2, z2);
glEnd();
glFlush();
}

// 绘制立方体
void DrawCube(void)
{
int i, j;
glBegin(GL_LINES);
for (i = 0; i < 12; ++i) // 12 条线段
{
for (j = 0; j < 2; ++j) // 每条线段 2个顶点
{
glVertex3fv(vertex_list[index_list[i][j]]);
}
}
glEnd();
glFlush();
}

void reshapeOperate()
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (ratio < 1)
glOrtho(-coordinatesize, coordinatesize, -coordinatesize / ratio, coordinatesize / ratio, -coordinatesize, coordinatesize);
else
glOrtho(-coordinatesize*ratio, coordinatesize*ratio, -coordinatesize, coordinatesize, -coordinatesize, coordinatesize);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

void reshape(int w, int h)
{
if ((w == 0) || (h == 0))
return;

glViewport(0, 0, w, h);

ratio = (GLfloat)w / (GLfloat)h;

reshapeOperate();
}

GLfloat AngleX = 45.0f;
GLfloat AngleY = 315.0f;

void reshape1(int w, int h)
{
GLfloat aspect = (GLfloat)w / (GLfloat)h;
GLfloat nRange = 100.0f;

glViewport(0, 0, w, h);

glMatrixMode(GL_PROJECTION); //将当前矩阵指定为投影模式
glLoadIdentity();

//设置三维投影区

if (w <= h)
{
glOrtho(-nRange, nRange, -nRange * aspect, nRange * aspect, -nRange, nRange);
}
else
{
glOrtho(-nRange, nRange, -nRange / aspect, nRange / aspect, -nRange, nRange);
}
}

void onDraw(void*)
{
// Draw something using OpenGL here
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除所有的像素

//glMatrixMode(GL_MODELVIEW);
//glLoadIdentity();
//glPushMatrix();

////glTranslatef(-0.2, 0, 0); // 平移
////glScalef(2, 1, 1); // 缩放

//glRotatef(xRotAngle, 1.0f, 0.0f, 0.0f);
//glRotatef(yRotAngle, 0.0f, 1.0f, 0.0f);
//glRotatef(zRotAngle, 0.0f, 0.0f, 1.0f);

//glColor3f(1, 0, 0);
//drawLine(0, 0, 0, MAX_X, 0, 0); //x轴

//glColor3f(0, 1, 0);
//drawLine(0, 0, 0, 0, MAX_Y, 0); //y轴

//glColor3f(0, 0, 1);
//drawLine(0, 0, 0, 0, 0, MAX_Z); //z轴

//times++;
//if (times > 1)
//{
// times = 0;
//}

//if (times % 1 == 0)
//{
// rotate += 0.3;
//}

//glRotatef(rotate, 0, 1, 0);
//glRotatef(rotate, 1, 0, 0);

//glColor3f(0, 1, 1);

//DrawCube();

//glPopMatrix();

reshape1(windowWidth, windowHeight);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
AngleX++;
AngleY++;
glPushMatrix();
{
glRotatef(AngleX, 1.0f, 0.0f, 0.0f);
glRotatef(AngleY, 0.0f, 1.0f, 0.0f);

glBegin(GL_POLYGON); //前表面
glColor3ub((GLubyte)255, (GLubyte)255, (GLubyte)255);//颜色设置为白色
glVertex3f(50.0f, 50.0f, 50.0f);

glColor3ub((GLubyte)255, (GLubyte)255, (GLubyte)0);//颜色设置为黄色
glVertex3f(50.0f, -50.0f, 50.0f);

glColor3ub((GLubyte)255, (GLubyte)0, (GLubyte)0);//颜色设置为红色
glVertex3f(-50.0f, -50.0f, 50.0f);

glColor3ub((GLubyte)255, (GLubyte)0, (GLubyte)255);//颜色设置为白色
glVertex3f(-50.0f, 50.0f, 50.0f);
glEnd();

glBegin(GL_POLYGON); //后表面
glColor3f(0.0f, 1.0f, 1.0f);//颜色设置为青色
glVertex3f(50.0f, 50.0f, -50.0f);

glColor3f(0.0f, 1.0f, 0.0f);//颜色设置为绿色
glVertex3f(50.0f, -50.0f, -50.0f);

glColor3f(0.0f, 0.0f, 0.0f);//颜色设置为黑色
glVertex3f(-50.0f, -50.0f, -50.0f);

glColor3f(0.0f, 0.0f, 1.0f);//颜色设置为蓝色
glVertex3f(-50.0f, 50.0f, -50.0f);
glEnd();

glBegin(GL_POLYGON); //上表面
glColor3d(0.0, 1.0, 1.0);//颜色设置为青色
glVertex3f(50.0f, 50.0f, -50.0f);

glColor3d(1.0, 1.0, 1.0);//颜色设置为白色
glVertex3f(50.0f, 50.0f, 50.0f);

glColor3d(1.0, 0.0, 1.0);//颜色设置为品红色
glVertex3f(-50.0f, 50.0f, 50.0f);

glColor3d(0.0, 0.0, 1.0);//颜色设置为蓝色
glVertex3f(-50.0f, 50.0f, -50.0f);
glEnd();

glBegin(GL_POLYGON); //下表面
glColor3ub(0u, 255u, 0u);//颜色设置为绿色
glVertex3f(50.0f, -50.0f, -50.0f);

glColor3ub(255u, 255u, 0u);//颜色设置为黄色
glVertex3f(50.0f, -50.0f, 50.0f);

glColor3ub(255u, 0u, 0u);//颜色设置为红色
glVertex3f(-50.0f, -50.0f, 50.0f);

glColor3ub(0u, 0u, 0u);//颜色设置为黑色
glVertex3f(-50.0f, -50.0f, -50.0f);
glEnd();

glBegin(GL_POLYGON); //左表面
glColor3ub((GLubyte)255, (GLubyte)255, (GLubyte)255);//颜色设置为白色
glVertex3f(50.0f, 50.0f, 50.0f);

glColor3ub((GLubyte)0, (GLubyte)255, (GLubyte)255);//颜色设置为青色
glVertex3f(50.0f, 50.0f, -50.0f);

glColor3ub((GLubyte)0, (GLubyte)255, (GLubyte)0);//颜色设置为绿色
glVertex3f(50.0f, -50.0f, -50.0f);

glColor3ub((GLubyte)255, (GLubyte)255, (GLubyte)0);//颜色设置为黄色
glVertex3f(50.0f, -50.0f, 50.0f);
glEnd();

glBegin(GL_POLYGON); //右表面
glColor3f(1.0f, 0.0f, 1.0f);//颜色设置为品红色
glVertex3f(-50.0f, 50.0f, 50.0f);

glColor3f(0.0f, 0.0f, 1.0f);//颜色设置为蓝色
glVertex3f(-50.0f, 50.0f, -50.0f);

glColor3f(0.0f, 0.0f, 0.0f);//颜色设置为黑色
glVertex3f(-50.0f, -50.0f, -50.0f);

glColor3f(1.0f, 0.0f, 0.0f);//颜色设置为红色
glVertex3f(-50.0f, -50.0f, 50.0f);
glEnd();
}
glPopMatrix();
}

void opencvWithOpenGLTest()
{
std::string openGLWindowName = "OpenGL Test";
cv::namedWindow(openGLWindowName, cv::WINDOW_OPENGL);
cv::resizeWindow(openGLWindowName, windowWidth, windowHeight);
cv::setOpenGlContext(openGLWindowName);
cv::setOpenGlDrawCallback(openGLWindowName, onDraw, NULL);

while (cv::waitKey(30) != 27)
{
cv::updateWindow(openGLWindowName); // when needed
}
}

int main(int argc, char *argv[])
{
opencvWithOpenGLTest();

return 0;
}

运行成功后可看到一个旋转的彩色立方体。

结论

  从实验结果来看,OpenCV 确实能支持 OpenGL 图形的显示,但其不足之处也很明显:没有提供鼠标和键盘的交互操作(可能是 Shaun 还没发现,毕竟只是尝试一下看它能不能显示),仅仅只是提供一个显示窗口。如果真想用 OpenGL 做一些好玩的东西,还是用 glut 和 glew 吧,不过 glut 已经停止更新许久,glew 在调试时可能会出现一些莫名其妙的错误,所以网上有人用 freeglut 代替 glut,glee 代替 glew,具体的东西 Shaun 也没试过,Shaun 目前还没做过 OpenGL 相关的事,这次用 OpenCV 显示 OpenGL 图形纯粹是为了好玩 (*^__^ *) 嘻嘻……。

后记

  本篇文档也是上次编译配置完 OpenCV-3.2 后做的一次小实验,但当时并没有记录,所以还有一些参考资料也已经不知道了 :-(。

参考资料

[1] OpenCV学习笔记(六十一)——建立支持OpenGL的OpenCV工程“Master OpenCV”chp.3http://blog.csdn.net/yang_xian521/article/category/910716

[2] 几个opengl立方体绘制案例http://blog.csdn.net/bcbobo21cn/article/category/3104565

Win10+VS2013+CMake-gui编译和配置OpenCV-3.2.0

Shaun 的系统环境:Win10_x64 英文企业版;VS2013-update5 英文旗舰版;CMake-3.6.3-win64-x64 免安装版;Qt-opensource-windows-x86-msvc2013-5.6.2。

  *注:Shaun 写的这篇文档主要用来编译 x86 版的动态 debug 库,想编译其它类型的库请自行参考其它资料,做相关改变。( 其实如果想编译 x64 版的可以在用 VS2013 编译时将上方的 Win32 平台选择 x64 平台;想编译 release 版的可以在用 VS2013 编译时将上方的 Debug 模式选择 Release 模式;想编译静态库的可以在用 CMake 生成时取消勾选 BUILD_SHARED_LIBS 选项即可。:-P )

前言

  因为 OpenCV-3.2 官方的 release 版只有支持 VS2015 的库,而且不包括扩展包( opencv_contrib )中的库,而由于某些历史原因,Shaun 目前使用的编译器还是 VS2013,又想用用扩展包中一些有趣的算法,在加上上个月 opencv-3.3 还没有正式 release,所以上个月 Shaun 就利用 VS2013 对 opencv-3.2 进行编译。具体编译过程如下:

Shaun 的系统环境:Win10_x64 英文企业版;VS2013-update5 英文旗舰版;CMake-3.6.3-win64-x64 免安装版;Qt-opensource-windows-x86-msvc2013-5.6.2。

  *注:Shaun 写的这篇文档主要用来编译 x86 版的动态 debug 库,想编译其它类型的库请自行参考其它资料,做相关改变。( 其实如果想编译 x64 版的可以在用 VS2013 编译时将上方的 Win32 平台选择 x64 平台;想编译 release 版的可以在用 VS2013 编译时将上方的 Debug 模式选择 Release 模式;想编译静态库的可以在用 CMake 生成时取消勾选 BUILD_SHARED_LIBS 选项即可。:-P )

前言

  因为 OpenCV-3.2 官方的 release 版只有支持 VS2015 的库,而且不包括扩展包( opencv_contrib )中的库,而由于某些历史原因,Shaun 目前使用的编译器还是 VS2013,又想用用扩展包中一些有趣的算法,在加上上个月 opencv-3.3 还没有正式 release,所以上个月 Shaun 就利用 VS2013 对 opencv-3.2 进行编译。具体编译过程如下:

准备篇

  先在 GitHub 上下载对应的 opencv 源码包:opencv-3.2.0opencv_contrib-3.2.0https://github.com/opencv),扩展包版本一定要和 opencv 版本相同。Shaun 为了添加 Qt 后端显示支持(为了好看和方便 :-P),所以还下载安装了支持 VS2013 的 Qt-5.6.2http://download.qt.io/archive/qt/)。再下载 CMake-3.6.3-win64-x64 免安装版https://cmake.org/files/)。至于微软的东西,推荐直接去 MSDN 我告诉你去下载。

  由于网上有的资料(具体是哪篇文章 Shaun 忘记了 o(╯□╰)o)说编译时的文件结构可能会影响编译是否成功,再加上为了方便编译管理,Shaun 编译时的文件结构为:

opencv-3.2.0_build
├── build
└── sources
​   ├── opencv-3.2.0
​   └── opencv_contrib-3.2.0

4 directories, 0 files

其中 opencv-3.2.0 用来装 opencv-3.2.0.zip 解压后的源码;opencv_contrib-3.2.0 用来装opencv_contrib-3.2.0.zip 解压后的源码;build 用来装 CMake 编译完成后的文件。

编译篇

  打开 /cmake-3.6.3-win64-x64/bin/cmake-gui.exe,在 Where is the source code 文本框中选择 /opencv-3.2.0_build/sources/opencv-3.2.0;在 Where to build the binaris 文本框中选择 /opencv-3.2.0_build/build,点击 Configure,在弹出的编译器选择框中选择 Visual Studio 12 2013,一直 Configure 直到红色的条变白。

  网上有人在这一步可能会出现ffmpeg not downloaded“ippicv_windows_20151201.zip”not downloaded这两个问题,Shaun 没出现这两个问题,所以没有机会验证 cmake-gui和vs2013编译opencv和opencv_contrib源码 中的解决办法是否正确。

  接下来就是添加扩展包,在白色条中找到 OPENCV_EXTRA_MODULES_PATH 文本框,在其中选择 opencv_contrib 源码中 modeles 所在路径:/opencv-3.2.0_build/sources/opencv_contrib-3.2.0/modules。

  至于想要支持 OpenGL 和 Qt 就需要勾选 WITH_OPENGLWITH_QTConfigure 后选择好 Qt 的安装目录,如果配置好 Qt 的环境变量 Cmake 将会自动选择好 Qt 所在路径。

  随后再次反复 Configure 直到界面不再出现红色背景,之后单击 Generate。不出意外的话,你会看到 Configure doneGenerate done

  Shaun 在这一步出现了 VS2013_CMake_opencv3.1动态库与静态库的配置与编译 中的问题,原因是同时勾选了同时勾选了 BUILD_opencv_worldBUILD_opencv_contirb_world,Shaun 的解决办法是将它们全部取消勾选,再次 ConfigureGenerate

  如果上面一切顺利的话就可以进行下一步了:使用 VS2013 编译 OpenCV。打开 /opencv-3.2.0_build/build 目录,将会看到一大堆文件和文件夹,双击 /opencv-3.2.0_build/build 目录下的 OpenCV.sln,用 VS2013 打开。找到 CMakeTargets 中的 INSTALL ,然后右键选择“Project Only”-->“Build Only INSTALL”。

漫长的等待。。。。。。 (╯﹏╰)b

  Shaun 在这一步出现了一个问题,具体问题和解决方法详见问题篇。

  一切顺利的话,应该会比 Shaun 下面的库多两个,Shaun 最后生成的 Debug 库为:

opencv_aruco320d.lib

opencv_bgsegm320d.lib

opencv_bioinspired320d.lib

opencv_calib3d320d.lib

opencv_ccalib320d.lib

opencv_core320d.lib

opencv_datasets320d.lib

opencv_dnn320d.lib

opencv_dpm320d.lib

opencv_face320d.lib

opencv_features2d320d.lib

opencv_flann320d.lib

opencv_fuzzy320d.lib

opencv_highgui320d.lib

opencv_imgcodecs320d.lib

opencv_imgproc320d.lib

opencv_line_descriptor320d.lib

opencv_ml320d.lib

opencv_objdetect320d.lib

opencv_optflow320d.lib

opencv_phase_unwrapping320d.lib

opencv_photo320d.lib

opencv_plot320d.lib

opencv_reg320d.lib

opencv_rgbd320d.lib

opencv_saliency320d.lib

opencv_shape320d.lib

opencv_stereo320d.lib

opencv_stitching320d.lib

opencv_structured_light320d.lib

opencv_superres320d.lib

opencv_surface_matching320d.lib

opencv_text320d.lib

opencv_tracking320d.lib

opencv_video320d.lib

opencv_videoio320d.lib

opencv_videostab320d.lib

opencv_xfeatures2d320d.lib

opencv_ximgproc320d.lib

opencv_xobjdetect320d.lib

opencv_xphoto320d.lib

共41个。

配置篇

  因为 Shaun 只编译了 x86 版动态 debug 库,所以以下环境配置都只针对 x86 版动态 debug 库。(其实要配置 x64 的库就只需将 x86 换成 x64 即可;要配置 release 模式的库就只需在添加附加依赖项中的库文件选择 release 模式的库(即数字后没有 d的 lib);若要配置静态库就需要选择静态库文件夹以及在附加依赖项中添加相应的静态库文件。:-P)

  首先把 /opencv-3.2.0_build/build/install 中的文件都提取出来,这和 OpenCV 官方 release 的 opencv 文件结构差不多,具体两层结构如下

.
├── bin
│  └── opencv_waldboost_detectord.exe
├── etc
│  ├── haarcascades
│  └── lbpcascades
├── include
│  ├── opencv
│  └── opencv2
├── LICENSE
├── OpenCVConfig.cmake
├── OpenCVConfig-version.cmake
└── x86
​   └── vc12

9 directories, 4 files

x86 文件夹就是 VS2013 生成的对应 VS 版本 32位 的各种库,include 文件夹就是 opencv 的各项模块。Shaun 将其中提取出的文件全部放入了 C:\Program Files\OpenCV\3.2.0\build 文件夹中。

  首先配置环境变量,系统(或用户)环境变量如下:

变量名变量值
PathC:\Program Files\OpenCV\3.2.0\build\x86\vc12\bin
OPENCVC:\Program Files\OpenCV\3.2.0\build

不然可能会报错:程序“XXXXXX”已退出,返回值为 -1073741701 (0xc000007b)。其中下面那行可以选择不要添加。

  然后在 VS 中配置环境。新建工程,然后在“属性管理器”中对应项目下 Debug | Win32 文件夹右键“添加新项目属性表”。(方便一次配置,多次使用,以后再使用只要在相应项目下右键“添加现有属性表”即可),Shaun 新项目属性表取名为:opencv-3.2.0_msvc2013_x86d.props。

接下来就是真正的 VS 环境配置了:

  双击打开刚才新建的属性表,选中“VC++目录”,注意在进行以下配置时建议都勾选左下角的“从父级或项目默认设置继承

可执行文件目录”中添加:

C:\Program Files\OpenCV\3.2.0\build\x86\vc12\bin

包含目录”中添加:

C:\Program Files\OpenCV\3.2.0\build\include

C:\Program Files\OpenCV\3.2.0\build\include\opencv

C:\Program Files\OpenCV\3.2.0\build\include\opencv2

库目录”中添加:

C:\Program Files\OpenCV\3.2.0\build\x86\vc12\lib

选中“链接器” –> “常规”,“附加库目录”中添加:

C:\Program Files\OpenCV\3.2.0\build\x86\vc12\lib

链接器” –> “输入”,“附加依赖项”中添加 C:\Program Files\OpenCV\3.2.0\build\x86\vc12\lib 中数字后带 d 的库文件,即编译篇中 Shaun 最后生成的 41 个库文件。

  配置完之后不要忘了右键该属性表进行保存处理,以便下个项目直接使用,不需要再重复进行配置。

最后附示例程序:

1
2
3
4
5
6
7
8
9
10
11
#include <opencv2/opencv.hpp>  

int main(int argc, char *argv[])
{
cv::Mat lena = cv::imread("lena.jpg"); //载入图像到Mat,jpg文件和该cpp在同一文件夹
cv::namedWindow("lena"); //创建一个名为 "lean"的窗口
cv::imshow("lena", lena); //显示名为 "lena"的窗口
cv::waitKey(5000); // 只对窗口机制起作用(显示5000ms,随后返回-1,即窗口关闭),若在此期间有按键按下,则马上返回按键的ASCII码。
//system("pause");
return 0;
}

这里必须在 imshow 后加入 waitkey,因为 WaitKey 不止是 Wait Key 而已,它其实还涉及到消息响应,有这个函数 cv 内部的 WndProc 函数才能起作用,才会更新窗口。

  最后程序运行成功并显示 lena 图,则说明编译和配置没问题。

问题篇

1、用 VS2013 编译 OpenCV 在漫长的等待阶段出现的问题。

  问题描述:CVV 模块报错,TS 模块编译不出来,好在这两个模块都不是很重要,可以忽略,Shaun 强迫症也没到这种程度 O(∩_∩)O~。

  解决办法: 在 CVV 模块报错后可在 CMake(不知道具体是 INSTALL 下的 CMake Rules 中的 INSTALL_force.rule,还是 ALL_BUILD 下的 CMakeLists.txt,忘记了 o(╯□╰)o)中添加 -DBUILD_opencv_cvv=OFF 忽略 CVV 模块,从而正常编译其它模块。参考 errors on build opencv with cvv module and qt5 #577。如果实在不行的话就在 CMake 生成的时候取消勾选出错模块,若是用 CMake 重新生成的话不要忘了先把 /opencv-3.2.0_build/build 目录下的文件全部删除干净。

后记

  这是以前写的两篇文档,现在再来整理成一篇。

附录

  既然能看到这里,说明是想在 VS 下使用 OpenCV,这里推荐一款 VS 下 OpenCV 开发调试神器:Image Watch,效果谁用谁知道。Image Watch 是 VS 的一个插件,不过它只支持 VS2012 及以上版本。使用方法为先设置断点( F9 ),随后在调试( F5 )模式下,鼠标指针悬停在 cv::Mat 类型变量上,即可出现 插件调试标签,点击查看图标即可显示相应图像。

参考资料

[1] cmake-gui和vs2013编译opencv和opencv_contrib源码http://livezingy.com/category/opencv/

[2] VS2013_CMake_opencv3.1动态库与静态库的配置与编译http://livezingy.com/category/opencv/

[3] 使用VS2015编译以及静态编译opencv3记录

[4] errors on build opencv with cvv module and qt5 #577

[5] VS2013中Image Watch插件的使用(OpenCV)http://blog.csdn.net/fengbingchun/article/category/721609