-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
2275 lines (2034 loc) · 181 KB
/
atom.xml
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
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[Quentin Bonnard]]></title>
<link href="http://quentin.bonnard.eu/atom.xml" rel="self"/>
<link href="http://quentin.bonnard.eu/"/>
<updated>2017-02-21T11:37:00+00:00</updated>
<id>http://quentin.bonnard.eu/</id>
<author>
<name><![CDATA[Quentin Bonnard]]></name>
</author>
<generator uri="http://octopress.org/">Octopress</generator>
<entry>
<title type="html"><![CDATA[Codeless sentiment analysis of HackerNews]]></title>
<link href="http://quentin.bonnard.eu/blog/2017/02/20/codeless-sentiment-analysis-of-hackernews/"/>
<updated>2017-02-20T00:00:00+00:00</updated>
<id>http://quentin.bonnard.eu/blog/2017/02/20/codeless-sentiment-analysis-of-hackernews</id>
<content type="html"><![CDATA[<p>This posts reports on a experiment with <a href="http://nodered.org/">Node-RED</a> (a project supported by the <a href="https://js.foundation/">JS Foundation</a>), using its visual data flow editor to fetch HN posts, filter those with positive words in the comments, and tweet them on <a href="http://twitter.com/hackergoodnews">@HackerGoodNews</a>.</p>
<h1>The flow</h1>
<p>I recently came across <a href="http://nodered.org/">Node-RED</a> and wondered how good it would be as a tool to automate a few things I find more and more annoying to do manually. A friend of mine challenged me to use it to tweet “positive” posts from Hacker News (mostly because there was a sentiment analysis node as part of the built-in set of nodes). Extra points for making it without code (even though there is a <code>function</code> node to write arbitrary javascript to process messages).</p>
<p>Here is the result:</p>
<p style="text-align:center;"> <img src="http://quentin.bonnard.eu/images/blog/2017/02/10/codeless-sentiment-analysis-of-hackernews.png" alt="A Node-RED flow to tweet HackerNews posts with positive words in the comments." /></p>
<p>It works as follows:</p>
<ul>
<li><code>Parse HN's RSS feed</code> creates a new <em>message</em> (Node-RED’s unit of processing) every time a new post appears in HN’s RSS feed.</li>
<li><code>limit 5 msg/s</code> ensures that no more that no more than 5 messages per second go through the rest of the flow. HackerNews seems to dislike too frequent requests, as made in the comment fetching node</li>
<li><code>delay 1 hour</code> gives some time for the comments to appear</li>
<li><code>fetch comments</code> requests the comment web page of the post</li>
<li><code>keep text only</code> extracts the comments from the web page</li>
<li><code>join</code> merges all the comments into one text</li>
<li><code>sentiment</code> analyses the text</li>
<li><code>positive only</code> lets only messages with a positive score through</li>
<li><code>title > 120 chars ?</code> switches messages depending on whether the title of the post is longer than 120 characters</li>
<li><code>truncate title</code> shortens the long title</li>
<li><code>long title</code> produces a tweet with the shortened title (adding suspension mark) and the url of the post</li>
<li><code>short title</code> simply produces a tweet with the title and the url of the post</li>
<li><code>Tweet</code> publishes the tweet on <a href="http://twitter.com/hackergoodnews">@hackergoodnews</a>.</li>
</ul>
<p>The sentiment analysis is very simple : it uses the <a href="http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6010">AFINN</a> word list where words or phrases have been manually evaluated between -5 and 5. For example, “you’re a terrific fascist” has a (positive) score of 2 points, because terific is worth <code>4</code>, and fascist <code>-2</code>… Well, I said it’s simple, not perfect (and to be honest, I browsed the list for a while to find this example ;) )</p>
<p>As a more honest example, here are the 5 most negative posts of last week-end:</p>
<ol>
<li><a href="https://www.susanjfowler.com/blog/2017/2/19/reflecting-on-one-very-strange-year-at-uber">Reflecting on One Very, Very Strange Year at Uber</a> (score: -117)</li>
<li><a href="https://www.nytimes.com/2017/02/19/business/uber-sexual-harassment-investigation.html">Uber Investigating Sexual Harassment Claims by Ex-Employee</a> (score: -77)</li>
<li><a href="http://www.cringely.com/2017/02/16/no-fracking-way-fukishima-daiichi-worse-ever/">Fukishima is Worse than Ever</a> (score: -60)</li>
<li><a href="https://www.theguardian.com/technology/2017/feb/20/uber-urgent-investigation-sexual-harassment-claims-susan-fowler">Uber launches ‘urgent investigation’ into sexual harassment claims</a> (score :-59)</li>
<li><a href="https://www.nytimes.com/2017/02/15/magazine/the-age-of-rudeness.html">The Age of Rudeness</a> (score: -48)</li>
</ol>
<p>And here are the 5 most positive posts:</p>
<ol>
<li><a href="https://news.distrokid.com/what-ive-been-up-to-for-the-past-4-years-ef06ab9c5cd6#.pbxjucbsm">I started a one-man biz that’s beating VC-backed startups</a> (score: 615)</li>
<li><a href="https://news.ycombinator.com/item?id=13668870">Ask HN: Non-technical readers of HN, why are you here?</a> (score: 273)</li>
<li><a href="https://reactos.org/project-news/reactos-044-released">ReactOS 0.4.4 Released</a> (score: 231)</li>
<li><a href="http://cityobservatory.org/lets-not-demonize-driving/">Let’s not demonize driving, just stop subsidizing it</a> (score: 175)</li>
<li><a href="https://blog.mozilla.org/addons/2017/02/16/the-road-to-firefox-57-compatibility-milestones/">Firefox 57 as the first release where only WebExtensions will be supported</a> (score: 127)</li>
</ol>
<h1>Conclusion</h1>
<p>Node-RED makes it really easy to build a tweeting bot that ignores sexual harassment and nuclear disasters without even coding (of course, that relies on a sane community commenting the news).</p>
<p>The only shortcoming I found concerns the feed parser which does not remember what was already processed. For example, if I were to restart my Node-RED instance, all the positive posts of the home page would be sent to twitter. That’s not a problem since Twitter detects and rejects duplicate statuses, but it would be more annoying if the messages were sent by email.</p>
<p>However, another (and maybe the biggest) strength of Node-RED is how easy it is to extend it by writing new nodes (with code, this time). That brings two solutions to my “problem”: either use one of the many existing nodes to hack some persistence into the feed parser (e.g. with a database node or a file node), or write my own feed parser. Good times ahead ! :)</p>
<h1>Comments</h1>
<p>Let’s see if <a href="https://news.ycombinator.com/item?id=13694830">the discussion of this article on HN</a> will be positive ;)</p>
<h1>Appendix</h1>
<p>Here is the flow to import into Node-RED, if you want to tinker with it:</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
<span class='line-number'>66</span>
<span class='line-number'>67</span>
<span class='line-number'>68</span>
<span class='line-number'>69</span>
<span class='line-number'>70</span>
<span class='line-number'>71</span>
<span class='line-number'>72</span>
<span class='line-number'>73</span>
<span class='line-number'>74</span>
<span class='line-number'>75</span>
<span class='line-number'>76</span>
<span class='line-number'>77</span>
<span class='line-number'>78</span>
<span class='line-number'>79</span>
<span class='line-number'>80</span>
<span class='line-number'>81</span>
<span class='line-number'>82</span>
<span class='line-number'>83</span>
<span class='line-number'>84</span>
<span class='line-number'>85</span>
<span class='line-number'>86</span>
<span class='line-number'>87</span>
<span class='line-number'>88</span>
<span class='line-number'>89</span>
<span class='line-number'>90</span>
<span class='line-number'>91</span>
<span class='line-number'>92</span>
<span class='line-number'>93</span>
<span class='line-number'>94</span>
<span class='line-number'>95</span>
<span class='line-number'>96</span>
<span class='line-number'>97</span>
<span class='line-number'>98</span>
<span class='line-number'>99</span>
<span class='line-number'>100</span>
<span class='line-number'>101</span>
<span class='line-number'>102</span>
<span class='line-number'>103</span>
<span class='line-number'>104</span>
<span class='line-number'>105</span>
<span class='line-number'>106</span>
<span class='line-number'>107</span>
<span class='line-number'>108</span>
<span class='line-number'>109</span>
<span class='line-number'>110</span>
<span class='line-number'>111</span>
<span class='line-number'>112</span>
<span class='line-number'>113</span>
<span class='line-number'>114</span>
<span class='line-number'>115</span>
<span class='line-number'>116</span>
<span class='line-number'>117</span>
<span class='line-number'>118</span>
<span class='line-number'>119</span>
<span class='line-number'>120</span>
<span class='line-number'>121</span>
<span class='line-number'>122</span>
<span class='line-number'>123</span>
<span class='line-number'>124</span>
<span class='line-number'>125</span>
<span class='line-number'>126</span>
<span class='line-number'>127</span>
<span class='line-number'>128</span>
<span class='line-number'>129</span>
<span class='line-number'>130</span>
<span class='line-number'>131</span>
<span class='line-number'>132</span>
<span class='line-number'>133</span>
<span class='line-number'>134</span>
<span class='line-number'>135</span>
<span class='line-number'>136</span>
<span class='line-number'>137</span>
<span class='line-number'>138</span>
<span class='line-number'>139</span>
<span class='line-number'>140</span>
<span class='line-number'>141</span>
<span class='line-number'>142</span>
<span class='line-number'>143</span>
<span class='line-number'>144</span>
<span class='line-number'>145</span>
<span class='line-number'>146</span>
<span class='line-number'>147</span>
<span class='line-number'>148</span>
<span class='line-number'>149</span>
<span class='line-number'>150</span>
<span class='line-number'>151</span>
<span class='line-number'>152</span>
<span class='line-number'>153</span>
<span class='line-number'>154</span>
<span class='line-number'>155</span>
<span class='line-number'>156</span>
<span class='line-number'>157</span>
<span class='line-number'>158</span>
<span class='line-number'>159</span>
<span class='line-number'>160</span>
<span class='line-number'>161</span>
<span class='line-number'>162</span>
<span class='line-number'>163</span>
<span class='line-number'>164</span>
<span class='line-number'>165</span>
<span class='line-number'>166</span>
<span class='line-number'>167</span>
<span class='line-number'>168</span>
<span class='line-number'>169</span>
<span class='line-number'>170</span>
<span class='line-number'>171</span>
<span class='line-number'>172</span>
<span class='line-number'>173</span>
<span class='line-number'>174</span>
<span class='line-number'>175</span>
<span class='line-number'>176</span>
<span class='line-number'>177</span>
<span class='line-number'>178</span>
<span class='line-number'>179</span>
<span class='line-number'>180</span>
<span class='line-number'>181</span>
<span class='line-number'>182</span>
<span class='line-number'>183</span>
<span class='line-number'>184</span>
<span class='line-number'>185</span>
<span class='line-number'>186</span>
<span class='line-number'>187</span>
<span class='line-number'>188</span>
<span class='line-number'>189</span>
<span class='line-number'>190</span>
<span class='line-number'>191</span>
<span class='line-number'>192</span>
<span class='line-number'>193</span>
<span class='line-number'>194</span>
<span class='line-number'>195</span>
<span class='line-number'>196</span>
<span class='line-number'>197</span>
<span class='line-number'>198</span>
<span class='line-number'>199</span>
<span class='line-number'>200</span>
<span class='line-number'>201</span>
<span class='line-number'>202</span>
<span class='line-number'>203</span>
<span class='line-number'>204</span>
<span class='line-number'>205</span>
<span class='line-number'>206</span>
<span class='line-number'>207</span>
<span class='line-number'>208</span>
<span class='line-number'>209</span>
<span class='line-number'>210</span>
<span class='line-number'>211</span>
<span class='line-number'>212</span>
<span class='line-number'>213</span>
<span class='line-number'>214</span>
<span class='line-number'>215</span>
<span class='line-number'>216</span>
<span class='line-number'>217</span>
<span class='line-number'>218</span>
<span class='line-number'>219</span>
<span class='line-number'>220</span>
<span class='line-number'>221</span>
<span class='line-number'>222</span>
<span class='line-number'>223</span>
<span class='line-number'>224</span>
<span class='line-number'>225</span>
<span class='line-number'>226</span>
<span class='line-number'>227</span>
<span class='line-number'>228</span>
<span class='line-number'>229</span>
<span class='line-number'>230</span>
<span class='line-number'>231</span>
<span class='line-number'>232</span>
<span class='line-number'>233</span>
<span class='line-number'>234</span>
<span class='line-number'>235</span>
<span class='line-number'>236</span>
<span class='line-number'>237</span>
<span class='line-number'>238</span>
<span class='line-number'>239</span>
<span class='line-number'>240</span>
<span class='line-number'>241</span>
<span class='line-number'>242</span>
<span class='line-number'>243</span>
<span class='line-number'>244</span>
<span class='line-number'>245</span>
<span class='line-number'>246</span>
<span class='line-number'>247</span>
<span class='line-number'>248</span>
<span class='line-number'>249</span>
<span class='line-number'>250</span>
<span class='line-number'>251</span>
<span class='line-number'>252</span>
<span class='line-number'>253</span>
<span class='line-number'>254</span>
<span class='line-number'>255</span>
<span class='line-number'>256</span>
<span class='line-number'>257</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>[
</span><span class='line'> {
</span><span class='line'> "id": "f24d4456.2acf58",
</span><span class='line'> "type": "feedparse",
</span><span class='line'> "z": "47e2e162.19bab8",
</span><span class='line'> "name": "Parse HN's RSS feed",
</span><span class='line'> "url": "https://news.ycombinator.com/rss",
</span><span class='line'> "interval": "1",
</span><span class='line'> "x": 346,
</span><span class='line'> "y": 222,
</span><span class='line'> "wires": [
</span><span class='line'> [
</span><span class='line'> "bbbfef8e.5229b8"
</span><span class='line'> ]
</span><span class='line'> ]
</span><span class='line'> },
</span><span class='line'> {
</span><span class='line'> "id": "194d8318.d5c7ad",
</span><span class='line'> "type": "http request",
</span><span class='line'> "z": "47e2e162.19bab8",
</span><span class='line'> "name": "fetch comments",
</span><span class='line'> "method": "GET",
</span><span class='line'> "ret": "txt",
</span><span class='line'> "url": "}",
</span><span class='line'> "tls": "",
</span><span class='line'> "x": 628.875,
</span><span class='line'> "y": 314.5,
</span><span class='line'> "wires": [
</span><span class='line'> [
</span><span class='line'> "8f2f189a.7c4f38"
</span><span class='line'> ]
</span><span class='line'> ]
</span><span class='line'> },
</span><span class='line'> {
</span><span class='line'> "id": "8feccdd1.b5b48",
</span><span class='line'> "type": "sentiment",
</span><span class='line'> "z": "47e2e162.19bab8",
</span><span class='line'> "name": "",
</span><span class='line'> "x": 311.125,
</span><span class='line'> "y": 522,
</span><span class='line'> "wires": [
</span><span class='line'> [
</span><span class='line'> "9fe15e58.b67db"
</span><span class='line'> ]
</span><span class='line'> ]
</span><span class='line'> },
</span><span class='line'> {
</span><span class='line'> "id": "8f2f189a.7c4f38",
</span><span class='line'> "type": "html",
</span><span class='line'> "z": "47e2e162.19bab8",
</span><span class='line'> "name": "keep text only",
</span><span class='line'> "tag": "div.comment span",
</span><span class='line'> "ret": "text",
</span><span class='line'> "as": "single",
</span><span class='line'> "x": 327.5,
</span><span class='line'> "y": 428,
</span><span class='line'> "wires": [
</span><span class='line'> [
</span><span class='line'> "24ff4198.fca71e"
</span><span class='line'> ]
</span><span class='line'> ]
</span><span class='line'> },
</span><span class='line'> {
</span><span class='line'> "id": "24ff4198.fca71e",
</span><span class='line'> "type": "join",
</span><span class='line'> "z": "47e2e162.19bab8",
</span><span class='line'> "name": "",
</span><span class='line'> "mode": "custom",
</span><span class='line'> "build": "string",
</span><span class='line'> "property": "payload",
</span><span class='line'> "propertyType": "msg",
</span><span class='line'> "key": "topic",
</span><span class='line'> "joiner": "\\n",
</span><span class='line'> "timeout": "",
</span><span class='line'> "count": "1",
</span><span class='line'> "x": 480.5,
</span><span class='line'> "y": 428,
</span><span class='line'> "wires": [
</span><span class='line'> [
</span><span class='line'> "8feccdd1.b5b48"
</span><span class='line'> ]
</span><span class='line'> ]
</span><span class='line'> },
</span><span class='line'> {
</span><span class='line'> "id": "9fe15e58.b67db",
</span><span class='line'> "type": "switch",
</span><span class='line'> "z": "47e2e162.19bab8",
</span><span class='line'> "name": "positive only",
</span><span class='line'> "property": "sentiment.score",
</span><span class='line'> "propertyType": "msg",
</span><span class='line'> "rules": [
</span><span class='line'> {
</span><span class='line'> "t": "gt",
</span><span class='line'> "v": "0",
</span><span class='line'> "vt": "num"
</span><span class='line'> }
</span><span class='line'> ],
</span><span class='line'> "checkall": "true",
</span><span class='line'> "outputs": 1,
</span><span class='line'> "x": 470.5,
</span><span class='line'> "y": 522,
</span><span class='line'> "wires": [
</span><span class='line'> [
</span><span class='line'> "a6426eab.664338"
</span><span class='line'> ]
</span><span class='line'> ]
</span><span class='line'> },
</span><span class='line'> {
</span><span class='line'> "id": "a62ee7eb.16d048",
</span><span class='line'> "type": "template",
</span><span class='line'> "z": "47e2e162.19bab8",
</span><span class='line'> "name": "short title",
</span><span class='line'> "field": "payload",
</span><span class='line'> "fieldType": "msg",
</span><span class='line'> "format": "handlebars",
</span><span class='line'> "syntax": "mustache",
</span><span class='line'> "template": "} }",
</span><span class='line'> "x": 686.5,
</span><span class='line'> "y": 654,
</span><span class='line'> "wires": [
</span><span class='line'> [
</span><span class='line'> "4c2dc9df.bb824"
</span><span class='line'> ]
</span><span class='line'> ]
</span><span class='line'> },
</span><span class='line'> {
</span><span class='line'> "id": "a6426eab.664338",
</span><span class='line'> "type": "switch",
</span><span class='line'> "z": "47e2e162.19bab8",
</span><span class='line'> "name": "title > 120 chars ?",
</span><span class='line'> "property": "article.title.length",
</span><span class='line'> "propertyType": "msg",
</span><span class='line'> "rules": [
</span><span class='line'> {
</span><span class='line'> "t": "gt",
</span><span class='line'> "v": "120",
</span><span class='line'> "vt": "num"
</span><span class='line'> },
</span><span class='line'> {
</span><span class='line'> "t": "else"
</span><span class='line'> }
</span><span class='line'> ],
</span><span class='line'> "checkall": "true",
</span><span class='line'> "outputs": 2,
</span><span class='line'> "x": 337.5,
</span><span class='line'> "y": 625,
</span><span class='line'> "wires": [
</span><span class='line'> [
</span><span class='line'> "fa7acd8f.799d68"
</span><span class='line'> ],
</span><span class='line'> [
</span><span class='line'> "a62ee7eb.16d048"
</span><span class='line'> ]
</span><span class='line'> ]
</span><span class='line'> },
</span><span class='line'> {
</span><span class='line'> "id": "da4cb6f0.97bfc8",
</span><span class='line'> "type": "template",
</span><span class='line'> "z": "47e2e162.19bab8",
</span><span class='line'> "name": "long title",
</span><span class='line'> "field": "payload",
</span><span class='line'> "fieldType": "msg",
</span><span class='line'> "format": "handlebars",
</span><span class='line'> "syntax": "mustache",
</span><span class='line'> "template": "}... }",
</span><span class='line'> "x": 688,
</span><span class='line'> "y": 605,
</span><span class='line'> "wires": [
</span><span class='line'> [
</span><span class='line'> "4c2dc9df.bb824"
</span><span class='line'> ]
</span><span class='line'> ]
</span><span class='line'> },
</span><span class='line'> {
</span><span class='line'> "id": "fa7acd8f.799d68",
</span><span class='line'> "type": "change",
</span><span class='line'> "z": "47e2e162.19bab8",
</span><span class='line'> "name": "truncate title",
</span><span class='line'> "rules": [
</span><span class='line'> {
</span><span class='line'> "t": "set",
</span><span class='line'> "p": "article.title",
</span><span class='line'> "pt": "msg",
</span><span class='line'> "to": "$substring(msg.article.title, 0, 120)",
</span><span class='line'> "tot": "jsonata"
</span><span class='line'> }
</span><span class='line'> ],
</span><span class='line'> "action": "",
</span><span class='line'> "property": "",
</span><span class='line'> "from": "",
</span><span class='line'> "to": "",
</span><span class='line'> "reg": false,
</span><span class='line'> "x": 537,
</span><span class='line'> "y": 605,
</span><span class='line'> "wires": [
</span><span class='line'> [
</span><span class='line'> "da4cb6f0.97bfc8"
</span><span class='line'> ]
</span><span class='line'> ]
</span><span class='line'> },
</span><span class='line'> {
</span><span class='line'> "id": "bbbfef8e.5229b8",
</span><span class='line'> "type": "delay",
</span><span class='line'> "z": "47e2e162.19bab8",
</span><span class='line'> "name": "",
</span><span class='line'> "pauseType": "rate",
</span><span class='line'> "timeout": "5",
</span><span class='line'> "timeoutUnits": "seconds",
</span><span class='line'> "rate": "5",
</span><span class='line'> "nbRateUnits": "1",
</span><span class='line'> "rateUnits": "second",
</span><span class='line'> "randomFirst": "100",
</span><span class='line'> "randomLast": "500",
</span><span class='line'> "randomUnits": "milliseconds",
</span><span class='line'> "drop": false,
</span><span class='line'> "x": 314.5,
</span><span class='line'> "y": 315,
</span><span class='line'> "wires": [
</span><span class='line'> [
</span><span class='line'> "536603f7.dd90c4"
</span><span class='line'> ]
</span><span class='line'> ]
</span><span class='line'> },
</span><span class='line'> {
</span><span class='line'> "id": "536603f7.dd90c4",
</span><span class='line'> "type": "delay",
</span><span class='line'> "z": "47e2e162.19bab8",
</span><span class='line'> "name": "",
</span><span class='line'> "pauseType": "delay",
</span><span class='line'> "timeout": "1",
</span><span class='line'> "timeoutUnits": "hours",
</span><span class='line'> "rate": "1",
</span><span class='line'> "nbRateUnits": "1",
</span><span class='line'> "rateUnits": "second",
</span><span class='line'> "randomFirst": "1",
</span><span class='line'> "randomLast": "5",
</span><span class='line'> "randomUnits": "seconds",
</span><span class='line'> "drop": false,
</span><span class='line'> "x": 464.5,
</span><span class='line'> "y": 315,
</span><span class='line'> "wires": [
</span><span class='line'> [
</span><span class='line'> "194d8318.d5c7ad"
</span><span class='line'> ]
</span><span class='line'> ]
</span><span class='line'> },
</span><span class='line'> {
</span><span class='line'> "id": "4c2dc9df.bb824",
</span><span class='line'> "type": "twitter out",
</span><span class='line'> "z": "47e2e162.19bab8",
</span><span class='line'> "twitter": "",
</span><span class='line'> "name": "Tweet",
</span><span class='line'> "x": 846.5,
</span><span class='line'> "y": 628,
</span><span class='line'> "wires": []
</span><span class='line'> }
</span><span class='line'>]</span></code></pre></td></tr></table></div></figure>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[2048 in Augmented Reality]]></title>
<link href="http://quentin.bonnard.eu/blog/2014/03/12/ar-2048-play-with-paper/"/>
<updated>2014-03-12T00:00:00+00:00</updated>
<id>http://quentin.bonnard.eu/blog/2014/03/12/ar-2048-play-with-paper</id>
<content type="html"><![CDATA[<p><a href="http://gabrielecirulli.github.io/2048/">2048</a> is a simple, wonderful HTML5 game where you can combine numbers on tiles, in order to sum them up to 2048. Don’t try it now, it’s too addictive and you’ll never come back to read the rest of this article.</p>
<p>Every day brings a new flavour of 2048 to Hacker News. After the <a href="http://ov3y.github.io/2048-AI/">AI version</a> and the <a href="http://emils.github.io/2048-multiplayer/">multiplayer version</a>, I propose the Augmented Reality version.</p>
<p>You can try the <a href="http://quentin.bonnard.eu/2048/">live demo in your browser</a>.</p>
<h2>How to play (the longer version)</h2>
<p>To play the augmented reality version:</p>
<ul>
<li><p>Go to the <a href="http://quentin.bonnard.eu/2048/">live demo</a> with Firefox or Chrome. Apparently Opera is also able to use the webcam. Forget Safari and IE… you weren’t seriously considering, were you ?</p></li>
<li><p>Share the webcam with the webpage. You can stay in your underwear, I swear I don’t upload any data.</p></li>
<li><p>Print the image of the tag at <a href="http://goo.gl/GWdcT2">http://goo.gl/GWdcT2</a>) or display it on your phone if you don’t have a printer. Here is a <a href="http://goo.gl/GWdcT2.qr">qr-code that links to the tag</a>, if you feel more like scanning it than typing the address.</p></li>
<li><p>Show the tag to make the game appear. Tilt it to make the tiles slide in one way or the other (up, down, right, left). See illustrations below.</p></li>
</ul>
<h2>How to play (the illustrated version)</h2>
<p>Controling an HTML5 game with pieces of paper is not exactly mainstream, so I will illustrate the interaction with pictures.</p>
<ol>
<li>Show the printed tag to the webcam:</li>
</ol>
<p style="text-align:center;"> <img src="http://quentin.bonnard.eu/images/blog/2014/03/12/example1.jpg" alt="Show the printed tag to the webcam" /></p>
<p>Or the digital version of the tag:</p>
<p style="text-align:center;"> <img src="http://quentin.bonnard.eu/images/blog/2014/03/12/example3.jpg" alt="Or the digital version of the tag" /></p>
<p>The game should replace the image of the tag:</p>
<p style="text-align:center;"> <img src="http://quentin.bonnard.eu/images/blog/2014/03/12/example2.jpg" alt="The game should replace the image of the tag" /></p>
<p>Tilt the tag/game up to send the number tiles up:</p>
<p style="text-align:center;"> <img src="http://quentin.bonnard.eu/images/blog/2014/03/12/example4.jpg" alt="Tilt the tag/game up to send the number tiles up" /></p>
<p>Tilt the tag/game left to send the number tiles left:</p>
<p style="text-align:center;"> <img src="http://quentin.bonnard.eu/images/blog/2014/03/12/example5.jpg" alt="Tilt the tag/game left to send the number tiles left" /></p>
<p>That’s it!</p>
<h2>Shortcomings</h2>
<p>The nice animations of the original 2048 are gone, because the game board is transformed into a canvas texture to display in the 3D scene. Another solution would be to transform the game board using CSS 3D transforms, and overlay it on the video… I may keep this for another afternoon hack.</p>
<h2>Hire me!</h2>
<p>If you need some help on a similar project, do not hesitate to <a href="http://quentin.bonnard.eu/contact">contact me !</a></p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Yes, my Kindle can do this: Flappy bird on paper]]></title>
<link href="http://quentin.bonnard.eu/blog/2014/03/03/Yes_my_Kindle_can_do_this_Flappy_bird_on_paper/"/>
<updated>2014-03-03T00:00:00+00:00</updated>
<id>http://quentin.bonnard.eu/blog/2014/03/03/Yes_my_Kindle_can_do_this_Flappy_bird_on_paper</id>
<content type="html"><![CDATA[<p>This video is a (joke) answer to <a href="http://www.visionect.si/blog/flappy-bird-on-e-paper/">Kindle users, can your reader do this: Flappy bird on e-paper</a></p>
<iframe width="560" height="315" src="http://quentin.bonnard.eu//www.youtube.com/embed/f6VjaOqbQCA" frameborder="0" allowfullscreen></iframe>
<p>Yes, I’m bad. You can try to do better on the <a href="http://quentin.bonnard.eu/dtmb/">live demo</a>. You will need to print (or display on your favourite device) <a href="https://github.com/qbonnard/qbonnard.github.io/raw/master/assets/2014-03-03/8.pdf">this tag</a>. The demo needs to access your webcam via the getUserMedia/Stream API, which means it works only on Firefox, Chrome and Opera. It should work on their mobile versions though. Firefox has the best performances, thanks to the asm.js optimisations.</p>
<h1>How does it work ?</h1>
<p>When Chilitag number 8 (why 8? no idea) is detected, the bird flaps. If the tag was continuously detected, the bird would take off like a rocket, so users naturally make flapping movements with their hand. This has been observed in 100% of the users at the time of this writing (I was alone).</p>
<h1>Why did you do that ?</h1>
<p>It’s fun, and I needed an excuse to toy with <a href="https://github.com/qbonnard/chilitags.js">chilitags.js</a>.</p>
<p>OK fine, I also hope this will get someone interested in <a href="http://quentin.bonnard.eu/contact">hiring me</a> to do similarly fun stuff.</p>
<h1>Credits</h1>
<p>This demo is based on <a href="https://github.com/marksteve/dtmb">Heavy Bird</a>. By based, I mean I only added a quick and dirty hack.</p>
<h1>Comments</h1>
<p>I posted <a href="https://news.ycombinator.com/item?id=7335593">this article on HackerNews</a>, if you care to comment.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Using OpenCV's test framework (with CMake)]]></title>
<link href="http://quentin.bonnard.eu/blog/2013/10/16/Using-OpenCV_s_test_framework_with_CMake/"/>
<updated>2013-10-16T00:00:00+00:00</updated>
<id>http://quentin.bonnard.eu/blog/2013/10/16/Using-OpenCV_s_test_framework_with_CMake</id>
<content type="html"><![CDATA[<p>This post explains how to set up tests making use of OpenCV’s built in framework.</p>
<h1>Introduction</h1>
<p>Promoting Chilitags, our C++ <a href="http://chili.epfl.ch/software">library for detecting fiducial markers</a>, from “research code” to published code means that we have no excuse any more for not having any test at all.
After a short comparisons for testing frameworks, it seemed that Google Test would best fit our need.</p>
<p>Chilitags is heavily based on OpenCV. It turns out that OpenCV already integrates Google Test, and adds their own features, so you don’t have to write a function to assert that two matrices are equal for example. I’m more and more conviced that “OpenCV” means “we already did everything you may and will need” in some secret language.</p>
<p>However, I failed to find documentation on OpenCV’s <a href="https://github.com/Itseez/opencv/tree/master/modules/ts"><code>ts</code> module</a>, even though they use it to test all the other modules. It is not listed in <a href="http://docs.opencv.org/">OpenCV’s documentation</a>, and Google didn’t help either. I asked on StackOverflow <a href="http://stackoverflow.com/questions/19405601/how-do-i-use-opencvs-test-framework-with-cmake">how to use OpenCV’s test framework with CMake</a>, but haven’t received an answer. This post aims at answering myself the question with what I found.</p>
<p>First, I’ll show how to configure CMake to compile an OpenCV’s executable, and then explain how to run several of them as part of CMake’s tests.</p>
<h1>Compiling an OpenCV test executable</h1>
<p>This section gives a minimal example of an OpenCV based library being tested by the OpenCV test framework. We give the code of a dummy library, short test codes, and the CMake set up to build them all.</p>
<h2>The code to be tested</h2>
<p>Let’s say we want to test a dummy library, whose interface is declared in <code>mylib.hpp</code>:</p>
<figure class='code'><figcaption><span> (mylib.hpp)</span> <a href='http://quentin.bonnard.eu/_includes/2013-10-16/opencv/lib/include/mylib.hpp'>download</a></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='cpp'><span class='line'><span class="cp">#ifndef mylib_hpp</span>
</span><span class='line'><span class="cp">#define mylib_hpp</span>
</span><span class='line'>
</span><span class='line'><span class="cp">#include <opencv2/core/core.hpp></span>
</span><span class='line'>
</span><span class='line'><span class="k">namespace</span> <span class="n">mylib</span> <span class="p">{</span>
</span><span class='line'> <span class="kt">void</span> <span class="n">myfunction</span> <span class="p">(</span><span class="n">cv</span><span class="o">::</span><span class="n">InputOutputArray</span> <span class="n">myparam</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="cp">#endif</span>
</span></code></pre></td></tr></table></div></figure>
<p>and implemented in <code>mylib.cpp</code>:</p>
<figure class='code'><figcaption><span> (mylib.cpp)</span> <a href='http://quentin.bonnard.eu/_includes/2013-10-16/opencv/lib/src/mylib.cpp'>download</a></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='cpp'><span class='line'><span class="cp">#include <mylib.hpp></span>
</span><span class='line'>
</span><span class='line'><span class="kt">void</span> <span class="n">mylib</span><span class="o">::</span><span class="n">myfunction</span> <span class="p">(</span><span class="n">cv</span><span class="o">::</span><span class="n">InputOutputArray</span> <span class="n">myparam</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="n">myparam</span><span class="p">.</span><span class="n">getMat</span><span class="p">()</span> <span class="o">=</span> <span class="n">cv</span><span class="o">::</span><span class="n">Scalar</span><span class="o">::</span><span class="n">all</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>Right… Obviously, this library is not the focus of this article.</p>
<h2>The test code</h2>
<p>We first write a simple test case in <code>mytest.cpp</code>:</p>
<figure class='code'><figcaption><span> (mytest.cpp)</span> <a href='http://quentin.bonnard.eu/_includes/2013-10-16/opencv/test/mytest.cpp'>download</a></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
</pre></td><td class='code'><pre><code class='cpp'><span class='line'><span class="cp">#include <opencv2/ts/ts.hpp></span>
</span><span class='line'><span class="cp">#include <mylib.hpp></span>
</span><span class='line'>
</span><span class='line'><span class="k">class</span> <span class="nc">MyTestClass</span> <span class="o">:</span> <span class="k">public</span> <span class="n">cvtest</span><span class="o">::</span><span class="n">BaseTest</span>
</span><span class='line'><span class="p">{</span>
</span><span class='line'><span class="k">public</span><span class="o">:</span>
</span><span class='line'> <span class="n">MyTestClass</span><span class="p">()</span><span class="o">:</span> <span class="n">mat</span><span class="p">(</span><span class="n">cv</span><span class="o">::</span><span class="n">Scalar</span><span class="o">::</span><span class="n">all</span><span class="p">(</span><span class="mi">1</span><span class="p">)){}</span>
</span><span class='line'>
</span><span class='line'><span class="k">protected</span><span class="o">:</span>
</span><span class='line'> <span class="n">cv</span><span class="o">::</span><span class="n">Mat</span> <span class="n">mat</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'> <span class="kt">void</span> <span class="n">run</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="n">ts</span><span class="o">-></span><span class="n">set_failed_test_info</span><span class="p">(</span><span class="n">cvtest</span><span class="o">::</span><span class="n">TS</span><span class="o">::</span><span class="n">OK</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'> <span class="c1">//check that all values are between 1 and 1 (and not Nan)</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="mi">0</span> <span class="o">!=</span> <span class="n">cvtest</span><span class="o">::</span><span class="n">check</span><span class="p">(</span><span class="n">mat</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="p">)</span>
</span><span class='line'> <span class="n">ts</span><span class="o">-></span><span class="n">set_failed_test_info</span><span class="p">(</span><span class="n">cvtest</span><span class="o">::</span><span class="n">TS</span><span class="o">::</span><span class="n">FAIL_INVALID_TEST_DATA</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'> <span class="n">mylib</span><span class="o">::</span><span class="n">myfunction</span><span class="p">(</span><span class="n">mat</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'> <span class="c1">//check that all values are between 0 and 0 (and not Nan)</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="mi">0</span> <span class="o">!=</span> <span class="n">cvtest</span><span class="o">::</span><span class="n">check</span><span class="p">(</span><span class="n">mat</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="p">)</span>
</span><span class='line'> <span class="n">ts</span><span class="o">-></span><span class="n">set_failed_test_info</span><span class="p">(</span><span class="n">cvtest</span><span class="o">::</span><span class="n">TS</span><span class="o">::</span><span class="n">FAIL_INVALID_OUTPUT</span><span class="p">);</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">};</span>
</span><span class='line'>
</span><span class='line'><span class="n">TEST</span><span class="p">(</span><span class="n">MyTestSuite</span><span class="p">,</span> <span class="n">ATestThatPasses</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="n">MyTestClass</span> <span class="n">myTestClass</span><span class="p">;</span>
</span><span class='line'> <span class="n">myTestClass</span><span class="p">.</span><span class="n">safe_run</span><span class="p">();</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">TEST</span><span class="p">(</span><span class="n">MyTestSuite</span><span class="p">,</span> <span class="n">ATestThatFails</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="kt">bool</span> <span class="n">mybool</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span><span class='line'> <span class="n">ASSERT_TRUE</span><span class="p">(</span><span class="n">mybool</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>This test cases will be automagically run in a specified folder if they are compiled with such <code>test_main.cpp</code>:</p>
<figure class='code'><figcaption><span> (test_main.cpp)</span> <a href='http://quentin.bonnard.eu/_includes/2013-10-16/opencv/test/test_main.cpp'>download</a></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='cpp'><span class='line'><span class="cp">#include <opencv2/ts/ts.hpp></span>
</span><span class='line'>
</span><span class='line'><span class="n">CV_TEST_MAIN</span><span class="p">(</span><span class="s">"."</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>
<p>That’s it!</p>
<h2>Structure and CMake Files</h2>
<p>All these files will be structured typically as follow:</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>mylib/
</span><span class='line'> CMakeLists.txt
</span><span class='line'> lib/
</span><span class='line'> CMakeLists.txt
</span><span class='line'> include/
</span><span class='line'> mylib.hpp
</span><span class='line'> src/
</span><span class='line'> mylib.cpp
</span><span class='line'> test/
</span><span class='line'> CMakeLists.txt
</span><span class='line'> mytest.cpp
</span><span class='line'> test_main.cpp</span></code></pre></td></tr></table></div></figure>
<p>The general CMakeLists is standard…</p>
<figure class='code'><figcaption><span> (CMakeLists.txt)</span> <a href='http://quentin.bonnard.eu/_includes/2013-10-16/opencv/CMakeLists.txt'>download</a></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class='cmake'><span class='line'><span class="nb">cmake_minimum_required</span><span class="p">(</span><span class="s">VERSION</span> <span class="s">2.6</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="nb">project</span><span class="p">(</span><span class="s">mylib</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="nb">include_directories</span><span class="p">(</span><span class="s">lib/include</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="nb">add_subdirectory</span><span class="p">(</span><span class="s">lib</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="nb">option</span> <span class="p">(</span><span class="s">WITH_TESTS</span> <span class="s2">"build tests"</span> <span class="s">ON</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="nb">if</span> <span class="p">(</span><span class="s">WITH_TESTS</span><span class="p">)</span>
</span><span class='line'> <span class="nb">add_subdirectory</span><span class="p">(</span><span class="s">test</span><span class="p">)</span>
</span><span class='line'><span class="nb">endif</span><span class="p">()</span>
</span></code></pre></td></tr></table></div></figure>
<p>…and so is the one to compile the library (in <code>src/CMakeLists.txt</code>):</p>
<figure class='code'><figcaption><span> (CMakeLists.txt)</span> <a href='http://quentin.bonnard.eu/_includes/2013-10-16/opencv/lib/CMakeLists.txt'>download</a></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='cmake'><span class='line'><span class="nb">file</span><span class="p">(</span><span class="s">GLOB_RECURSE</span> <span class="s">mylib_source</span> <span class="s">src/*</span><span class="p">)</span>
</span><span class='line'><span class="nb">add_library</span><span class="p">(</span><span class="s">mylib</span> <span class="s">SHARED</span> <span class="o">${</span><span class="nv">mylib_source</span><span class="o">}</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="nb">find_package</span><span class="p">(</span><span class="s">OpenCV</span> <span class="s">REQUIRED</span> <span class="s">core</span><span class="p">)</span>
</span><span class='line'><span class="nb">target_link_libraries</span><span class="p">(</span><span class="s">mylib</span> <span class="o">${</span><span class="nv">OpenCV_LIBS</span><span class="o">}</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>
<p>Last but not least, it is not much more complicated to compile an OpenCV test:</p>
<figure class='code'><figcaption><span> (CMakeLists.txt)</span> <a href='http://quentin.bonnard.eu/_includes/2013-10-16/opencv/test/CMakeLists.txt'>download</a></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='cmake'><span class='line'><span class="nb">file</span><span class="p">(</span><span class="s">GLOB</span> <span class="s">mytest_source_files</span> <span class="s">*.cpp</span><span class="p">)</span>
</span><span class='line'><span class="nb">add_executable</span><span class="p">(</span><span class="s">mytest</span> <span class="o">${</span><span class="nv">mytest_source_files</span><span class="o">}</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="nb">target_link_libraries</span><span class="p">(</span><span class="s">mytest</span> <span class="s">mylib</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="nb">find_package</span><span class="p">(</span> <span class="s">OpenCV</span> <span class="s">REQUIRED</span> <span class="s">ts</span><span class="p">)</span>
</span><span class='line'><span class="nb">target_link_libraries</span><span class="p">(</span> <span class="s">mytest</span> <span class="o">${</span><span class="nv">OpenCV_LIBS</span><span class="o">}</span> <span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>
<p>And we’re done.</p>
<h2>Running the tests</h2>
<p>Finally we can compile averything and run the test in a standard CMake process. Here is the one if you use Makefile:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='sh'><span class='line'>mkdir build
</span><span class='line'><span class="nb">cd </span>build
</span><span class='line'>cmake ..
</span><span class='line'>make
</span></code></pre></td></tr></table></div></figure>
<p>This creates a <code>mytest</code> executable in <code>build/test</code> which can be run to produce this output:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
</pre></td><td class='code'><pre><code class='sh'><span class='line'><span class="o">[==========]</span> Running 2 tests from 1 <span class="nb">test </span><span class="k">case</span>.
</span><span class='line'><span class="o">[</span>----------<span class="o">]</span> Global <span class="nb">test </span>environment <span class="nb">set</span>-up.