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

<channel>
	<title>橋本商会 &#187; cpp</title>
	<atom:link href="http://shokai.org/blog/archives/tag/cpp/feed" rel="self" type="application/rss+xml" />
	<link>http://shokai.org/blog</link>
	<description>なんか作ったりした記録を忘れないうちに書くblog</description>
	<lastBuildDate>Mon, 06 Feb 2012 13:32:44 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
<atom:link rel="hub" href="http://pubsubhubbub.appspot.com/" />
			<item>
		<title>scansnapで自炊した本をkindleで読めるように補正する(2)</title>
		<link>http://shokai.org/blog/archives/5337</link>
		<comments>http://shokai.org/blog/archives/5337#comments</comments>
		<pubDate>Tue, 14 Sep 2010 12:16:33 +0000</pubDate>
		<dc:creator>shokai</dc:creator>
				<category><![CDATA[未分類]]></category>
		<category><![CDATA[cpp]]></category>
		<category><![CDATA[Kindle]]></category>
		<category><![CDATA[kindlize_images]]></category>
		<category><![CDATA[OpenCV]]></category>
		<category><![CDATA[scansnap]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[画像処理]]></category>

		<guid isPermaLink="false">http://shokai.org/blog/?p=5337</guid>
		<description><![CDATA[橋本商会 scansnapで自炊した本をkindleで読めるように補正するをOpenCV使ってC++で書き直したら60倍速くなった。635ページのオライリーの本が約1分半で処理できたし、画質も前のJRuby版より少し読み [...]]]></description>
			<content:encoded><![CDATA[<p>
<a href="http://shokai.org/blog/archives/5327">橋本商会  scansnapで自炊した本をkindleで読めるように補正する</a>をOpenCV使ってC++で書き直したら60倍速くなった。635ページのオライリーの本が約1分半で処理できたし、画質も前のJRuby版より少し読みやすくなった気がする。<br />
<br />
<br />
こういう事をする。<br />
</p>
<blockquote><p>
■やっている事<br />
scansnapで自炊した本をそのままkindleで表示するには色々問題がある。<br />
<ul>
  <li>kindleの解像度は800×600だけど、画面内にページ位置等のUIが入るし画面を縦横回転させたり拡大縮小できるので、表示時にkindle上でリサイズされる。この時、線が細くて薄い部分が消滅してしまって、文字がかすれて読めなくなってしまう。</li>
  <li>scansnapで取り込んだままのPDFファイルでは、コントラストが弱くてとても読みづらい。</li>
  <li>ページの上下左右の余白が邪魔で、kindleに全画面表示した時に小さくなってしまう</li>
</ul>
<br />
kindle上でリサイズされる事を前提として、文字が消えてしまうような細い部分を太くしてやるしかない。<br />
しょうがないので、コントラストを上げたり、拡大縮小されるのを前提として先にアンチエイリアス？的な処理をしたり、余白を切り取ったり、リサイズしたりするようにした。<br />
上下左右を裁ち落とし→リサイズ→グレースケール化→2値化→黒の周りを#999999で太らせるという処理をしている。<br />
</p></blockquote>
<p>
<br />
上下左右裁ち落とし→グレースケール化→2値化→黒の周りを#000000で太らせる→リサイズ<br />
という処理に変えた。JRuby+javax.imageioでピクセル単位に処理すると、解像度が大きい画像を処理するのが苦しかったので先にリサイズするようにしていた。<br />
OpenCVは十分速いので、高解像度のまま処理して最後に縮小するように変更した。これでサイズが大きい本でも綺麗に変換できる。<br />
<br />
<br />
■使う<br />
OpenCV1.00以上と、boostのboost::system, boost::filesystem, boost::program_optionsのインストールが必要。少なくともMacとUbuntuでは動く。<br />
その辺は<a href="http://github.com/shokai/scansnap_adjust_images_kindle">github/shokai/scansnap_adjust_images_kindle</a>に書いた。<br />
<br />
<br />
gitリポジトリを持ってきてコンパイルする<br />
<pre class="prettyprint">
git clone git://github.com/shokai/scansnap_adjust_images_kindle.git<br />
cd scansnap_adjust_images_kindle/kindlize_images<br />
<br />
# for Mac<br />
make -f Makefile.macosx<br />
</pre>
<br />
./configureの作り方がよくわからないのでとりあえずMac用のMakefileだけ置いてある。<br />
Linuxとかでも、opencv、boost::filesystem、boost::progoram_optionsをインストールしてあればコンパイルはできる。(この組み合わせは色々なOSでよく使ってる)<br />
<br />
<br />
オライリーの判型の本をkindle用に補正する。上下左右の余白を削除して白黒になって文字が太くなる。イラストは見づらくなるかもしれない。<br />
<pre class="prettyprint">
mkdir ~/tmp/mybook_kindle<br />
./kindlize_images --help<br />
./kindlize_images -i ~/tmp/mybook/ -o ~/tmp/mybook_kindle/ -t 190 -w 1200 -h 1600 --cleft 120 --cright 120 --ctop 150 --cbottom 150<br />
</pre>
<br />
<br />
はじめにPDFから連番のJPEG画像に書き出して、それからこのツールを使って変換して、最後に連番画像をPDFにまとめるという使い方を想定している。<br />
PDFから連番画像を書き出す方法は、前の記事やgithubのREADME.mdに書いた。<br />
連番画像を1つのPDFへ結合するのは、gitリポジトリの中に一緒にimages2pdf.appというautomatorアプリを入れて置いたのでそれを使うと良い。<br />
<br />
<br />
■ソースコード<br />
100行切った。boost::filesystemのおかげでディレクトリ内一括処理が書きやすかった。<br />
JRuby使って書くよりもコードが35行短くなったけど、最初に試行錯誤するにはJRuby+javax.imageioでやる方が手軽だったな。<br />
<br />
<a href="http://github.com/shokai/scansnap_adjust_images_kindle/blob/master/kindlize_images/kindlize_images.cpp">kindlize_images/kindlize_images.cpp at master from shokai&apos;s scansnap_adjust_images_kindle &#8211; GitHub</a><br />
<pre class="prettyprint">
#include "cv.h"<br />
#include "highgui.h"<br />
#include &lt;boost/program_options.hpp&gt;<br />
#include &lt;boost/filesystem/operations.hpp&gt;<br />
#include &lt;boost/filesystem/path.hpp&gt;<br />
#include &lt;boost/filesystem/fstream.hpp&gt;<br />
#include &lt;iostream&gt;<br />
using namespace boost;<br />
using namespace std;<br />
namespace fs = boost::filesystem;<br />
<br />
IplImage *adjust_image(IplImage *img, program_options::variables_map argmap){<br />
  int cleft = argmap["cleft"].as&lt;int&gt;();<br />
  int cright = argmap["cright"].as&lt;int&gt;();<br />
  int ctop = argmap["ctop"].as&lt;int&gt;();<br />
  int cbottom = argmap["cbottom"].as&lt;int&gt;();<br />
<br />
  const int w = img-&gt;width - cleft - cright;<br />
  const int h = img-&gt;height - ctop - cbottom;<br />
  <br />
  cvSetImageROI(img, cvRect(cleft, ctop, w, h));<br />
<br />
  IplImage *img_gray = cvCreateImage(cvSize(w,h), IPL_DEPTH_8U, 1);<br />
  cvCvtColor(img, img_gray, CV_BGR2GRAY);<br />
<br />
  // 2値化<br />
  IplImage *img_bin = cvCreateImage(cvSize(w,h), IPL_DEPTH_8U, 1);<br />
  cvThreshold(img_gray, img_bin, argmap["threshold"].as&lt;int&gt;(), 255, CV_THRESH_BINARY);<br />
<br />
  // 文字を太らせる<br />
  cvErode(img_bin, img_bin, NULL, 1);<br />
<br />
  // リサイズ<br />
  int width = argmap["width"].as&lt;int&gt;();<br />
  int height = argmap["height"].as&lt;int&gt;();<br />
  IplImage *img_resized;<br />
  double scale = ((double)h)/w;<br />
  if(((double)height)/width &lt; scale){ // 縦長<br />
    img_resized = cvCreateImage(cvSize((int)(height/scale), height), IPL_DEPTH_8U, 1);<br />
  }<br />
  else{ // 横長<br />
    img_resized = cvCreateImage(cvSize(width, (int)(scale*width)), IPL_DEPTH_8U, 1);<br />
  }<br />
  cvResize(img_bin, img_resized, CV_INTER_LINEAR);<br />
<br />
  cvReleaseImage(&amp;img_bin);<br />
  cvReleaseImage(&amp;img_gray);<br />
  return img_resized;<br />
}<br />
<br />
int main(int argc, char* argv[]) {<br />
  program_options::options_description opts("options");<br />
  opts.add_options()<br />
    ("help", "ヘルプを表示")<br />
    ("width,w", program_options::value&lt;int&gt;(), "output width")<br />
    ("height,h", program_options::value&lt;int&gt;(), "output height")<br />
    ("threshold,t", program_options::value&lt;int&gt;(), "binarize threshold")<br />
    ("input,i", program_options::value&lt;string&gt;(), "input directory name")<br />
    ("output,o", program_options::value&lt;string&gt;(), "output directory name")<br />
    ("cleft", program_options::value&lt;int&gt;(), "crop left (pixel)")<br />
    ("cright", program_options::value&lt;int&gt;(), "crop right (pixel)")<br />
    ("ctop", program_options::value&lt;int&gt;(), "crop top (pixel)")<br />
    ("cbottom", program_options::value&lt;int&gt;(), "crop bottom (pixel)");<br />
  program_options::variables_map argmap;<br />
  program_options::store(parse_command_line(argc, argv, opts), argmap);<br />
  program_options::notify(argmap);<br />
  if (argmap.count("help") || !argmap.count("input") || !argmap.count("output") ||<br />
      !argmap.count("threshold")) {<br />
    cerr &lt;&lt; "[input, output] required" &lt;&lt; endl;<br />
    cerr &lt;&lt; opts &lt;&lt; endl;<br />
    return 1;<br />
  }<br />
<br />
  string in_dir = argmap["input"].as&lt;string&gt;();<br />
  fs::path path = complete(fs::path(in_dir, fs::native));<br />
  fs::directory_iterator end;<br />
  for (fs::directory_iterator i(path); i!=end; i++){<br />
    string img_fullname = in_dir + i-&gt;leaf();<br />
    cout &lt;&lt; img_fullname &lt;&lt; endl;<br />
    IplImage *img, *img_result;<br />
    img = cvLoadImage(img_fullname.c_str());<br />
    if(!img){<br />
      cerr &lt;&lt; "image file load error" &lt;&lt; endl;<br />
    }<br />
    else{<br />
      img_result = adjust_image(img, argmap);<br />
      string out_filename = argmap["output"].as&lt;string&gt;() + "/" + i-&gt;leaf();<br />
      cvSaveImage(out_filename.c_str(), img_result);<br />
      cvReleaseImage(&amp;img);<br />
      cvReleaseImage(&amp;img_result);<br />
    }    <br />
  }<br />
}<br />
</pre>
</p>
]]></content:encoded>
			<wfw:commentRss>http://shokai.org/blog/archives/5337/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>ZeroMQでOpenCV cvOpticalFlowのデータを配信する</title>
		<link>http://shokai.org/blog/archives/5311</link>
		<comments>http://shokai.org/blog/archives/5311#comments</comments>
		<pubDate>Wed, 11 Aug 2010 10:39:22 +0000</pubDate>
		<dc:creator>shokai</dc:creator>
				<category><![CDATA[未分類]]></category>
		<category><![CDATA[cpp]]></category>
		<category><![CDATA[OpenCV]]></category>
		<category><![CDATA[OpticalFlow]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[ZeroMQ]]></category>
		<category><![CDATA[画像処理]]></category>

		<guid isPermaLink="false">http://shokai.org/blog/?p=5311</guid>
		<description><![CDATA[1VQ9がZeroMQで遊んでたので、俺も橋本商会 cvCalcOpticalFlowBMをZeroMQでpubしてみた。ZeroMQはなんか面倒な事を適当にやってくれるmessaging libraryで、色々な言語の [...]]]></description>
			<content:encoded><![CDATA[<p>
1VQ9がZeroMQで遊んでたので、俺も<a href="http://shokai.org/blog/archives/4820">橋本商会  cvCalcOpticalFlowBM</a>をZeroMQでpubしてみた。<a href="http://www.zeromq.org/">ZeroMQ</a>はなんか面倒な事を適当にやってくれるmessaging libraryで、色々な言語のバインディングが出ている。<br />
<br />
ZeroMQのpubはセンサーのデータとかを垂れ流しにするのに都合がよさそう。<br />
clientが何台いるかどうかを考えないで良いし、pub/subどちらが先に起動していても適当に接続処理をしてくれる。cookbookを見てるとmulticastやthread間通信にも使ってる。とりあえずセンサーデータ垂れ流しという用途に俺はよく使いそう。<br />
<br />
<br />
ソースコードは<a href="http://github.com/shokai/zeromq-study">githubに置いた</a>。<br />
他にも<a href="http://github.com/shokai/zeromq-study/tree/7ac74e64a0343ea87f55b94fb26baa29cf652668/count">単純なカウントアップのpub/sub両方をC++/C/Rubyで書いた（6種）</a>のと、<a href="http://github.com/shokai/zeromq-study/tree/7ac74e64a0343ea87f55b94fb26baa29cf652668/twitter-stream">twitterのstream APIをZMQ_PUBで中継する</a>のを作ってみた（解説：<a href="http://d.hatena.ne.jp/shokai/20100810/1281455530">zeromqインストール、twitter stream APIを中継 &#8211; 橋本詳解</a>）。特にstream APIのHUB的存在は便利。<br />
<br />
あと、<a href="http://mongrel2.org/">mongrel2</a>が<a href="http://nichol.as/zeromq-an-introduction">WebSocketやXMLSocketとZeroMQの接続をしてくれるようになる</a>らしくて期待してる。<br />
<br />
<br />
<br />
受信側<br />
<a href="http://github.com/shokai/zeromq-study/blob/d2f3e4f69881a517a9c0445372b7b366e0a74f7b/opticalflow/opticalflow_sub.rb">opticalflow_sub.rb</a><br />
<pre class="prettyprint">
#!/usr/bin/env ruby<br />
require 'rubygems'<br />
require 'zmq'<br />
<br />
ctx = ZMQ::Context.new<br />
sock= ctx.socket(ZMQ::SUB)<br />
sock.connect('tcp://127.0.0.1:5000')<br />
sock.setsockopt(ZMQ::SUBSCRIBE, 'opticalflow')<br />
<br />
loop do<br />
  puts sock.recv()<br />
end<br />
</pre>
<br />
<br />
送信側。これを適当なパソンコにUSBカメラ刺して動かしておけば、別のマシンから動きが取れる！!<br />
<a href="http://github.com/shokai/zeromq-study/blob/d2f3e4f69881a517a9c0445372b7b366e0a74f7b/opticalflow/opticalflow_pub.cpp">opticalflow_pub.cpp</a><br />
<pre class="prettyprint">
// http://opencv.jp/sample/optical_flow.html<br />
#include &lt;cv.h&gt;<br />
#include &lt;highgui.h&gt;<br />
#include &lt;cxcore.h&gt;<br />
#include &lt;ctype.h&gt;<br />
#include &lt;stdio.h&gt;<br />
#include &lt;iostream&gt;<br />
#include &lt;boost/format.hpp&gt;<br />
#include &lt;zmq.hpp&gt;<br />
<br />
using namespace std;<br />
using namespace boost;<br />
<br />
void detect_flow(IplImage *img, IplImage *img_p, IplImage *dst);<br />
zmq::context_t ctx(1);<br />
zmq::socket_t sock(ctx, ZMQ_PUB);<br />
<br />
int main(int argc, char* argv[]) {<br />
  IplImage *img = NULL;<br />
  CvCapture *capture = NULL;<br />
  capture = cvCreateCameraCapture(0);<br />
  //capture = cvCaptureFromAVI("test.mov");<br />
  if(capture == NULL){<br />
    cerr &lt;&lt; "capture device not found!!" &lt;&lt; endl;<br />
    return -1;<br />
  }<br />
<br />
  sock.bind("tcp://127.0.0.1:5000");<br />
<br />
  CvSize size = cvSize(320, 240);<br />
  IplImage *img_resized = cvCreateImage(size, IPL_DEPTH_8U, 3);<br />
  IplImage *img_gray = cvCreateImage(size, IPL_DEPTH_8U, 1);<br />
  IplImage *img_gray_p = cvCreateImage(size, IPL_DEPTH_8U, 1);<br />
  IplImage *img_dst = cvCreateImage(size, IPL_DEPTH_8U, 3);<br />
<br />
  char winNameCapture[] = "Capture";<br />
  cvNamedWindow(winNameCapture, CV_WINDOW_AUTOSIZE);<br />
  <br />
  while (1) {<br />
    img = cvQueryFrame(capture);<br />
    cvResize(img, img_resized);<br />
    cvCvtColor(img_resized, img_gray, CV_BGR2GRAY);<br />
    cvCopy(img_resized, img_dst);<br />
    detect_flow(img_gray, img_gray_p, img_dst);<br />
    cvShowImage(winNameCapture, img_dst);<br />
    cvCopy(img_gray, img_gray_p);<br />
    if (cvWaitKey(10) == 'q') break;<br />
  }<br />
  <br />
  cvReleaseCapture(&amp;capture);<br />
  cvDestroyWindow(winNameCapture);<br />
  <br />
  return 0;<br />
}<br />
<br />
void detect_flow(IplImage *src_img1, IplImage *src_img2, IplImage *dst_img){<br />
  int i, j, dx, dy, rows, cols;<br />
  int block_size = 24;<br />
  int shift_size = 10;<br />
  CvMat *velx, *vely;<br />
  CvSize block = cvSize(block_size, block_size);<br />
  CvSize shift = cvSize(shift_size, shift_size);<br />
  CvSize max_range = cvSize(50, 50);<br />
<br />
  rows = int(ceil (double (src_img1-&gt;height) / block_size));<br />
  cols = int(ceil (double (src_img1-&gt;width) / block_size));<br />
  velx = cvCreateMat(rows, cols, CV_32FC1);<br />
  vely = cvCreateMat(rows, cols, CV_32FC1);<br />
  cvSetZero(velx);<br />
  cvSetZero(vely);<br />
<br />
  cvCalcOpticalFlowBM(src_img1, src_img2, block, shift, max_range, 0, velx, vely);<br />
  string result_str = string("");<br />
  for (i = 0; i &lt; velx-&gt;width; i++) {<br />
    for (j = 0; j &lt; vely-&gt;height; j++) {<br />
      dx = (int)cvGetReal2D(velx, j, i);<br />
      dy = (int)cvGetReal2D(vely, j, i);<br />
      cvLine(dst_img, cvPoint(i * block_size, j * block_size),<br />
              cvPoint(i * block_size + dx, j * block_size + dy), CV_RGB(255, 0, 0), 1, CV_AA, 0);<br />
      if(dx != 0 || dy != 0){<br />
	result_str += str(format("[%d,%d,%d,%d]") % (i*block_size) % (j*block_size) % dx % dy);<br />
      }<br />
    }<br />
  }<br />
  if(result_str.size() &gt; 0){<br />
    result_str = str(format("opticalflow %s") % result_str);<br />
    cout &lt;&lt; result_str &lt;&lt; endl;<br />
    zmq::message_t msg(result_str.size()+1); // ZeroMQ<br />
    memcpy(msg.data(), result_str.c_str(), result_str.size()+1);<br />
    sock.send(msg);<br />
  }<br />
}<br />
</pre>
<br />
<br />
<pre>
g++ -O opticalflow_pub.cpp -o opticalflow_pub.bin -I/opt/local/include/opencv -lcv -lcvaux -lcxcore -lhighgui  -I/usr/local/include /usr/local/lib/libzmq.a<br />
</pre>
<br />
<br />
これで動いた座標とその方向 [x,y,dx,dy] が連続で送られてくる。<br />
<pre>
opticalflow [48,216,4,-29][72,216,0,-29][96,216,0,-29][264,216,-9,-29]<br />
opticalflow [48,216,4,-29][96,216,0,-29][120,216,0,-29][264,216,-9,-29]<br />
opticalflow [48,216,4,-29][96,168,0,10][96,192,-10,-20][96,216,0,-29][120,192,0,10][120,216,0,-29][144,216,0,-29][168,216,0,-29][192,48,-10,0][192,216,0,-29][216,216,0,-29][264,216,-9,-29]<br />
opticalflow [96,168,0,10][96,192,-10,-10][96,216,0,-29][120,168,0,10][120,192,0,10][120,216,0,-29][144,216,0,-29][168,48,0,10][168,96,0,10][168,216,0,-29][192,72,0,40][192,96,0,-30][192,216,0,-29][264,216,-9,-29]<br />
opticalflow [48,216,4,-29][96,168,0,10][96,216,0,-29][120,168,0,10][120,192,0,10][120,216,0,-29][144,216,0,-29][168,48,10,0][168,96,0,10][168,216,0,-29][192,96,0,-30][192,216,0,-29][264,216,-9,-29]<br />
</pre>
</p>
]]></content:encoded>
			<wfw:commentRss>http://shokai.org/blog/archives/5311/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>OpenCVで画像サイズを求めるgearman workerをdaemontoolsで管理する</title>
		<link>http://shokai.org/blog/archives/5254</link>
		<comments>http://shokai.org/blog/archives/5254#comments</comments>
		<pubDate>Sun, 25 Jul 2010 16:02:54 +0000</pubDate>
		<dc:creator>shokai</dc:creator>
				<category><![CDATA[未分類]]></category>
		<category><![CDATA[boost]]></category>
		<category><![CDATA[cpp]]></category>
		<category><![CDATA[gearman]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[json_builder.h]]></category>
		<category><![CDATA[OpenCV]]></category>
		<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://shokai.org/blog/?p=5254</guid>
		<description><![CDATA[OpenCVで画像のサイズを求めるgearman workerを作って、Rubyから呼ぶで作ったworkerをpreforkさせて、そいつらをdaemontoolsで管理できるようにした。あらかじめCPU個数+いくつかf [...]]]></description>
			<content:encoded><![CDATA[<p>
<a href="http://shokai.org/blog/archives/4987">OpenCVで画像のサイズを求めるgearman workerを作って、Rubyから呼ぶ</a>で作ったworkerをpreforkさせて、そいつらをdaemontoolsで管理できるようにした。あらかじめCPU個数+いくつかforkしておくと、CPUが複数あるマシンを生かせるし、解析前にlibcurlで画像を取得している時のI/O待ちが少なくなって良い。（この記事のworkerはlibcurl使ってない版だけど）<br />
あと、返り値は自分で作った<a href="http://github.com/shokai/json_builder-cpp">json_builder.h</a>を使って返すようにした。<br />
<br />
なにげに大量の画像の中からダウンロード失敗した破損画像を見つけるのに重宝している。<br />
<br />
まずdaemontoolsをインストールしておく<br />
<ul>
  <li><a href="http://d.hatena.ne.jp/shokai/20100724/1279996402">Macにdaemontoolsインストール &#8211; 橋本詳解</a></li>
  <li><a href="http://d.hatena.ne.jp/shokai/20100725/1280014778">Ubuntu10.04にdaemontoolsインストール &#8211; 橋本詳解</a></li>
</ul>
gearmandもdaemontoolsで自動起動するようにしておく。<br />
<br />
<br />
daemontoolsで管理できるようにする。<br />
普通にforkしただけだと、daemontoolsでsvc -dしてプロセスを止めようとしてもforkした子プロセスの方が止まらない。<br />
<br />
Perlの場合の良い例があった。<br />
<a href="http://d.hatena.ne.jp/tokuhirom/20100201/1264989237">How to manage Gearman worker processes. &#8211; TokuLog 改メ tokuhirom’s blog</a><br />
<a href="http://search.cpan.org/~kazuho/Parallel-Prefork/lib/Parallel/Prefork.pm">Parallel::Prefork</a>を使っている。Parallel::Preforkのソースを読んでみたら、trap_signalsオプションで親プロセスがSIGTERMとSIGHUPをフックして、子プロセスにkillを送っていた。<br />
よく考えたら普通のforkで親が子を殺すというやつだった。<br />
<br />
<br />
Parallel::Preforkと同じ様にやる。<br />
forkした後親が子のpidのリストを持っておいて、SIGTERM/SIGHUPをフックして、子を全部killする処理を追加した。<br />
<br />
daemontoolsのrunスクリプトはこれ<br />
<pre class="prettyprint">
#!/bin/sh<br />
exec&nbsp;2&gt;&amp;1<br />
exec&nbsp;setuidgid&nbsp;sho&nbsp;/Users/sho/src/gearmand-study/imgsize/imgsizeWorker&nbsp;-s&nbsp;localhost&nbsp;-p&nbsp;7003&nbsp;--fork&nbsp;5<br />
</pre>
起動すると5個にプロセスが増える。親はdaemontoolsのsuperviseが管理してくれる。<br />
これでsvc -dとか-uとかすればまとめて起動終了するようになった。<br />
<br />
<a href="http://shokai.org/projects/gearman-study/index.cgi/file/6b0da8ba1769/imgsize/imgsizeWorker.cpp">imgsizeWorker.cpp</a><br />
<pre class="prettyprint">
//&nbsp;画像サイズを返すgearman&nbsp;worker<br />
#include&nbsp;&lt;stdio.h&gt;<br />
#include&nbsp;&lt;stdlib.h&gt;<br />
#include&nbsp;&lt;unistd.h&gt;<br />
#include&nbsp;&lt;signal.h&gt;<br />
#include&nbsp;&lt;string&gt;<br />
#include&nbsp;&lt;iostream&gt;<br />
#include&nbsp;&lt;cv.h&gt;<br />
#include&nbsp;&lt;highgui.h&gt;<br />
#include&nbsp;&lt;boost/program_options.hpp&gt;<br />
#include&nbsp;&lt;boost/regex.hpp&gt;<br />
#include&nbsp;&lt;boost/format.hpp&gt;<br />
#include&nbsp;&lt;boost/tuple/tuple.hpp&gt;<br />
#include&nbsp;&lt;boost/tuple/tuple_io.hpp&gt;<br />
#include&nbsp;&lt;boost/any.hpp&gt;<br />
#include&nbsp;&lt;libgearman/gearman.h&gt;<br />
#include&nbsp;"json_builder.h"<br />
<br />
using&nbsp;namespace&nbsp;boost;<br />
using&nbsp;namespace&nbsp;std;<br />
<br />
tuple&lt;int,&nbsp;int&gt;&nbsp;get_size(const&nbsp;string&amp;&nbsp;fileName);&nbsp;//&nbsp;画像のwidth,heightを返す<br />
map&lt;string,any&gt;&nbsp;imgsize(const&nbsp;string&amp;&nbsp;fileName);&nbsp;//&nbsp;gearman&nbsp;workerとしてclientに返すためのJSON&nbsp;Objectを作る<br />
void&nbsp;*job_imgsize(gearman_job_st&nbsp;*job,&nbsp;void&nbsp;*cb_arg,&nbsp;size_t&nbsp;*result_size,&nbsp;gearman_return_t&nbsp;*ret_ptr);<br />
void&nbsp;on_exit_signal(int&nbsp;sig);<br />
vector&lt;int&gt;&nbsp;pids;<br />
<br />
int&nbsp;main(int&nbsp;argc,&nbsp;char*&nbsp;argv[])&nbsp;{<br />
&nbsp;&nbsp;program_options::options_description&nbsp;opts("options");<br />
&nbsp;&nbsp;opts.add_options()<br />
&nbsp;&nbsp;&nbsp;&nbsp;("help,h",&nbsp;"helpを表示")<br />
&nbsp;&nbsp;&nbsp;&nbsp;("server,s",&nbsp;program_options::value&lt;string&gt;(),&nbsp;"gearmanサーバーのアドレス")<br />
&nbsp;&nbsp;&nbsp;&nbsp;("port,p",&nbsp;program_options::value&lt;int&gt;(),&nbsp;"gearmanサーバーのport番号")<br />
&nbsp;&nbsp;&nbsp;&nbsp;("fork",&nbsp;program_options::value&lt;int&gt;(),&nbsp;"preforkする数")<br />
&nbsp;&nbsp;&nbsp;&nbsp;("test,t",&nbsp;program_options::value&lt;string&gt;(),&nbsp;"gearman&nbsp;worker単体テスト用query");<br />
&nbsp;&nbsp;program_options::variables_map&nbsp;argmap;<br />
&nbsp;&nbsp;program_options::store(parse_command_line(argc,&nbsp;argv,&nbsp;opts),&nbsp;argmap);<br />
&nbsp;&nbsp;program_options::notify(argmap);<br />
<br />
&nbsp;&nbsp;if(!argmap.count("help")){<br />
&nbsp;&nbsp;&nbsp;&nbsp;if(argmap.count("test")){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;"---test---"&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string&nbsp;gearman_param&nbsp;=&nbsp;argmap["test"].as&lt;string&gt;();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;json_builder::toJson(imgsize(gearman_param))&nbsp;&lt;&lt;&nbsp;endl;&nbsp;//&nbsp;単体でworkerとしてのテスト<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}else&nbsp;if(argmap.count("server")&nbsp;&amp;&amp;&nbsp;argmap.count("port")){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(argmap.count("fork")){<br />
	int&nbsp;i,&nbsp;pid;<br />
	for(i&nbsp;=&nbsp;1;&nbsp;i&nbsp;&lt;&nbsp;argmap["fork"].as&lt;int&gt;();&nbsp;i++){<br />
	&nbsp;&nbsp;pid&nbsp;=&nbsp;fork();<br />
	&nbsp;&nbsp;if(pid&nbsp;==&nbsp;0){&nbsp;//&nbsp;子プロセス<br />
	&nbsp;&nbsp;&nbsp;&nbsp;pids.clear();<br />
	&nbsp;&nbsp;&nbsp;&nbsp;break;<br />
	&nbsp;&nbsp;}<br />
	&nbsp;&nbsp;else{&nbsp;//&nbsp;親プロセス<br />
	&nbsp;&nbsp;&nbsp;&nbsp;pids.push_back(pid);<br />
	&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;str(format("fork:%d&nbsp;-&nbsp;parent:%d&nbsp;child:%d")&nbsp;%&nbsp;<br />
			i&nbsp;%<br />
			getpid()&nbsp;%<br />
			pid)&nbsp;&lt;&lt;&nbsp;endl;<br />
	&nbsp;&nbsp;}<br />
	}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(pids.size()&nbsp;&gt;&nbsp;0){&nbsp;//&nbsp;親プロセスの終了シグナルをフックする<br />
	signal(SIGTERM,&nbsp;on_exit_signal);<br />
	signal(SIGHUP,&nbsp;on_exit_signal);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gearman_worker_st&nbsp;worker;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gearman_worker_create(&amp;worker);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string&nbsp;g_server&nbsp;=&nbsp;argmap["server"].as&lt;string&gt;();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;g_port&nbsp;=&nbsp;argmap["port"].as&lt;int&gt;();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;hostent&nbsp;*g_host&nbsp;=&nbsp;gethostbyname((char*)g_server.c_str());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string&nbsp;g_server_addr&nbsp;=&nbsp;str(format("%d.%d.%d.%d")&nbsp;%<br />
				&nbsp;(uint)(uchar)g_host-&gt;h_addr[0]&nbsp;%<br />
				&nbsp;(uint)(uchar)g_host-&gt;h_addr[1]&nbsp;%<br />
				&nbsp;(uint)(uchar)g_host-&gt;h_addr[2]&nbsp;%<br />
				&nbsp;(uint)(uchar)g_host-&gt;h_addr[3]);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gearman_worker_add_server(&amp;worker,&nbsp;g_server_addr.c_str(),&nbsp;g_port);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gearman_worker_add_function(&amp;worker,&nbsp;"img_size",&nbsp;0,&nbsp;job_imgsize,&nbsp;NULL);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;str(format("---start&nbsp;worker&nbsp;(%s:%d)---")&nbsp;%<br />
		&nbsp;&nbsp;g_server_addr&nbsp;%&nbsp;g_port)&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while(true)&nbsp;gearman_worker_work(&amp;worker);&nbsp;//&nbsp;workerとして待機<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;cerr&nbsp;&lt;&lt;&nbsp;"server,portが必要です"&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;cerr&nbsp;&lt;&lt;&nbsp;opts&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;return&nbsp;1;<br />
&nbsp;&nbsp;<br />
}<br />
<br />
//&nbsp;opencvで画像サイズを取得<br />
tuple&lt;int,&nbsp;int&gt;&nbsp;get_size(const&nbsp;string&amp;&nbsp;fileName){<br />
&nbsp;&nbsp;IplImage&nbsp;*img&nbsp;=&nbsp;cvLoadImage(fileName.c_str());<br />
&nbsp;&nbsp;if(!img){<br />
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;make_tuple(-1,&nbsp;-1);<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;else{<br />
&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;width&nbsp;=&nbsp;img-&gt;width;<br />
&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;height&nbsp;=&nbsp;img-&gt;height;<br />
&nbsp;&nbsp;&nbsp;&nbsp;cvReleaseImage(&amp;img);<br />
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;make_tuple(width,&nbsp;height);<br />
&nbsp;&nbsp;}<br />
}<br />
<br />
//&nbsp;画像サイズを取得してgearman&nbsp;serverに返すJSON&nbsp;Objectを作る<br />
map&lt;string,any&gt;&nbsp;imgsize(const&nbsp;string&amp;&nbsp;fileName){<br />
&nbsp;&nbsp;map&lt;string,any&gt;&nbsp;result_m;<br />
&nbsp;&nbsp;int&nbsp;width,&nbsp;height;<br />
&nbsp;&nbsp;tie(width,&nbsp;height)&nbsp;=&nbsp;get_size(fileName);<br />
&nbsp;&nbsp;if(width&nbsp;&gt;&nbsp;0&nbsp;&amp;&amp;&nbsp;height&nbsp;&gt;&nbsp;0){<br />
&nbsp;&nbsp;&nbsp;&nbsp;result_m["width"]&nbsp;=&nbsp;width;<br />
&nbsp;&nbsp;&nbsp;&nbsp;result_m["height"]&nbsp;=&nbsp;height;<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;else{<br />
&nbsp;&nbsp;&nbsp;&nbsp;result_m["error"]&nbsp;=&nbsp;string("image&nbsp;load&nbsp;error");<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;return&nbsp;result_m;<br />
}<br />
<br />
//&nbsp;gearman&nbsp;worker&nbsp;job<br />
void&nbsp;*job_imgsize(gearman_job_st&nbsp;*job,&nbsp;void&nbsp;*cb_arg,&nbsp;size_t&nbsp;*result_size,&nbsp;gearman_return_t&nbsp;*ret_ptr){<br />
&nbsp;&nbsp;string&nbsp;fileName&nbsp;=&nbsp;(char*)gearman_job_workload(job);<br />
&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;fileName&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;string&nbsp;result_str&nbsp;=&nbsp;json_builder::toJson(imgsize(fileName));<br />
&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;"&nbsp;=&gt;&nbsp;"&nbsp;&lt;&lt;&nbsp;result_str&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;char&nbsp;*result&nbsp;=&nbsp;(char*)strdup(result_str.c_str());<br />
&nbsp;&nbsp;*result_size&nbsp;=&nbsp;result_str.size();<br />
&nbsp;&nbsp;*ret_ptr&nbsp;=&nbsp;GEARMAN_SUCCESS;<br />
&nbsp;&nbsp;return&nbsp;result;<br />
}<br />
<br />
void&nbsp;on_exit_signal(int&nbsp;sig){<br />
&nbsp;&nbsp;for(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;pids.size();&nbsp;i++){<br />
&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;str(format("kill&nbsp;(pid:%d)")&nbsp;%&nbsp;pids[i])&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;&nbsp;&nbsp;if(kill(pids[i],&nbsp;SIGKILL)&nbsp;&lt;&nbsp;0){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cerr&nbsp;&lt;&lt;&nbsp;str(format("kill&nbsp;failed&nbsp;(pid:%d)")&nbsp;%&nbsp;pids[i])&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;exit(0);<br />
}<br />
</pre>
</p>
]]></content:encoded>
			<wfw:commentRss>http://shokai.org/blog/archives/5254/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>json_buiilderをgithubに移動させた</title>
		<link>http://shokai.org/blog/archives/5199</link>
		<comments>http://shokai.org/blog/archives/5199#comments</comments>
		<pubDate>Tue, 15 Jun 2010 16:01:02 +0000</pubDate>
		<dc:creator>shokai</dc:creator>
				<category><![CDATA[未分類]]></category>
		<category><![CDATA[cpp]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[json_builder.h]]></category>
		<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://shokai.org/blog/?p=5199</guid>
		<description><![CDATA[bitbucketからhttp://github.com/shokai/json_builder-cppに移動した。 ちょっとまじめにREADMEを書いたりした。 githubのreadmeにはmarkdown形式を使っ [...]]]></description>
			<content:encoded><![CDATA[<p>
bitbucketから<a href="http://github.com/shokai/json_builder-cpp">http://github.com/shokai/json_builder-cpp</a>に移動した。<br />
<br />
ちょっとまじめにREADMEを書いたりした。<br />
githubのreadmeにはmarkdown形式を使った。<a href="http://d.hatena.ne.jp/shokai/20100608/1275998764">markdownをインストールすればローカルでもマークアップがプレビューできて良い</a><br />
<br />
json_builder.hはヘッダファイル単体でstd::mapやvectorをjsonにシリアライズするC++用のライブラリ。parseはできない。</p>
]]></content:encoded>
			<wfw:commentRss>http://shokai.org/blog/archives/5199/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>json_builderを特殊文字のエスケープ、true、false、nullに対応させた</title>
		<link>http://shokai.org/blog/archives/5046</link>
		<comments>http://shokai.org/blog/archives/5046#comments</comments>
		<pubDate>Sat, 30 Jan 2010 01:56:23 +0000</pubDate>
		<dc:creator>shokai</dc:creator>
				<category><![CDATA[未分類]]></category>
		<category><![CDATA[cpp]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[json_builder.h]]></category>
		<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://shokai.org/blog/?p=5046</guid>
		<description><![CDATA[前：橋本商会 C++でmapやvectorをJSON出力するjson_builder.hを作った ダブルクオートなどを含む文字列を値に保持するためのエスケープ処理にboost::regexを使ったので、libboost_ [...]]]></description>
			<content:encoded><![CDATA[<p>
前：<a href="http://shokai.org/blog/archives/5021">橋本商会  C++でmapやvectorをJSON出力するjson_builder.hを作った</a><br />
<br />
<br />
ダブルクオートなどを含む文字列を値に保持するためのエスケープ処理にboost::regexを使ったので、libboost_regex-mt.aをコンパイル時に読み込まないとならなくなった → <a href="http://bitbucket.org/shokai/json-builder/src/abbc9d0a382d/test/Makefile">Makefileの例</a><br />
まさかboost::regex_replaceで頭にバックスラッシュをつけるのに、<a href="http://d.hatena.ne.jp/shokai/20100127/1264606980">バックスラッシュ4つで置換</a>するとは思わなかった<br />
<br />
<br />
こんな風に使う。true, false, nullを入れられるようになった<br />
<a href="http://bitbucket.org/shokai/json-builder/src/abbc9d0a382d/test/test.cpp">test.cpp</a><br />
<pre class="prettyprint">
#include&nbsp;&lt;iostream&gt;<br />
#include&nbsp;&lt;string&gt;<br />
#include&nbsp;&lt;map&gt;<br />
#include&nbsp;&lt;boost/any.hpp&gt;<br />
#include&nbsp;"../json_builder.h"<br />
<br />
int&nbsp;main(int&nbsp;argc,&nbsp;char*&nbsp;argv[]){<br />
&nbsp;&nbsp;std::map&lt;string,boost::any&gt;&nbsp;user;<br />
&nbsp;&nbsp;user["name"]&nbsp;=&nbsp;std::string("shokai");<br />
&nbsp;&nbsp;user["fullname"]&nbsp;=&nbsp;std::string("sho&nbsp;hashimoto");<br />
&nbsp;&nbsp;user["age"]&nbsp;=&nbsp;25;<br />
&nbsp;&nbsp;user["test"]&nbsp;=&nbsp;1.23;<br />
&nbsp;&nbsp;user["null"]&nbsp;=&nbsp;json_builder::null;<br />
&nbsp;&nbsp;user["true"]&nbsp;=&nbsp;true;<br />
&nbsp;&nbsp;user["false"]&nbsp;=&nbsp;false;<br />
<br />
&nbsp;&nbsp;string&nbsp;json&nbsp;=&nbsp;json_builder::toJson(user);<br />
&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;json&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;return&nbsp;0;<br />
}<br />
</pre>
<br />
<br />
実行結果<br />
<pre>
{"age":"25","false":false,"fullname":"sho hashimoto","name":"shokai","null":null,"test":"1.23","true":true}<br />
</pre>
<br />
<br />
<br />
C++でnullを表現するために適当な構造体を定義してしまったけど、こういうので良いんだろうか？<br />
<a href="http://bitbucket.org/shokai/json-builder/src/abbc9d0a382d/json_builder.h">json_builder.h</a><br />
<pre class="prettyprint">
#include&nbsp;&lt;iostream&gt;<br />
#include&nbsp;&lt;string&gt;<br />
#include&nbsp;&lt;map&gt;<br />
#include&nbsp;&lt;vector&gt;<br />
#include&nbsp;&lt;boost/any.hpp&gt;<br />
#include&nbsp;&lt;boost/tuple/tuple.hpp&gt;<br />
#include&nbsp;&lt;boost/format.hpp&gt;<br />
#include&nbsp;&lt;boost/foreach.hpp&gt;<br />
#include&nbsp;&lt;boost/regex.hpp&gt;<br />
<br />
using&nbsp;namespace&nbsp;std;<br />
using&nbsp;namespace&nbsp;boost;<br />
<br />
#define&nbsp;null&nbsp;json_null()<br />
<br />
namespace&nbsp;json_builder{<br />
&nbsp;&nbsp;struct&nbsp;json_null{};<br />
<br />
&nbsp;&nbsp;string&nbsp;toJson(any&nbsp;value){<br />
&nbsp;&nbsp;&nbsp;&nbsp;if(value.type()&nbsp;==&nbsp;typeid(vector&lt;any&gt;)){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string&nbsp;result_str;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vector&lt;any&gt;&nbsp;vec&nbsp;=&nbsp;any_cast&lt;vector&lt;any&gt;&nbsp;&gt;(value);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;vec.size();&nbsp;i++){<br />
	result_str&nbsp;+=&nbsp;toJson(vec[i]);<br />
	if(i&nbsp;&lt;&nbsp;vec.size()-1)&nbsp;result_str&nbsp;+=&nbsp;",";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result_str&nbsp;=&nbsp;str(format("[%s]")&nbsp;%&nbsp;result_str);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;result_str;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;else&nbsp;if(value.type()&nbsp;==&nbsp;typeid(map&lt;string,any&gt;)){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string&nbsp;result_str;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;map&lt;string,any&gt;&nbsp;m&nbsp;=&nbsp;any_cast&lt;map&lt;string,any&gt;&nbsp;&gt;(value);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string&nbsp;key;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;any&nbsp;value;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;i&nbsp;=&nbsp;0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BOOST_FOREACH(tie(key,value),&nbsp;m){<br />
	result_str&nbsp;+=&nbsp;str(format("\"%s\":%s")&nbsp;%&nbsp;key&nbsp;%&nbsp;toJson(value));<br />
	if(++i&nbsp;&lt;&nbsp;m.size())&nbsp;result_str&nbsp;+=&nbsp;",";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result_str&nbsp;=&nbsp;str(format("{%s}")&nbsp;%&nbsp;result_str);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;result_str;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;else&nbsp;if(value.type()&nbsp;==&nbsp;typeid(json_null)){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;string("null");<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;else&nbsp;if(value.type()&nbsp;==&nbsp;typeid(string)){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;str(format("\"%s\"")&nbsp;%&nbsp;<br />
		&nbsp;&nbsp;&nbsp;regex_replace(any_cast&lt;string&gt;(value),&nbsp;regex("[\"\'\\\\/]"),&nbsp;"\\\\$0"));<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;else&nbsp;if(value.type()&nbsp;==&nbsp;typeid(bool)){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(any_cast&lt;bool&gt;(value))&nbsp;return&nbsp;string("true");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;string("false");<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;else&nbsp;if(value.type()&nbsp;==&nbsp;typeid(int)){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;str(format("\"%d\"")&nbsp;%&nbsp;any_cast&lt;int&gt;(value));<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;else&nbsp;if(value.type()&nbsp;==&nbsp;typeid(double)){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;str(format("\"%d\"")&nbsp;%&nbsp;any_cast&lt;double&gt;(value));<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
<br />
}<br />
</pre>
<br />
</p>
]]></content:encoded>
			<wfw:commentRss>http://shokai.org/blog/archives/5046/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>C++でmapやvectorをJSON出力するjson_builder.hを作った</title>
		<link>http://shokai.org/blog/archives/5021</link>
		<comments>http://shokai.org/blog/archives/5021#comments</comments>
		<pubDate>Wed, 27 Jan 2010 04:08:06 +0000</pubDate>
		<dc:creator>shokai</dc:creator>
				<category><![CDATA[未分類]]></category>
		<category><![CDATA[cpp]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[json_builder.h]]></category>
		<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://shokai.org/blog/?p=5021</guid>
		<description><![CDATA[C++でJSONというと、json.orgにもライブラリがたくさん紹介されているとおり色々ある。でも単に出力するだけの物で、ヘッダファイル一つで簡単に使えるのが無かったので作ってみた。 ちょっとstringの連結コストが [...]]]></description>
			<content:encoded><![CDATA[<p>
C++でJSONというと、<a href="http://www.json.org/json-ja.html">json.org</a>にもライブラリがたくさん紹介されているとおり色々ある。でも単に出力するだけの物で、ヘッダファイル一つで簡単に使えるのが無かったので作ってみた。<br />
ちょっとstringの連結コストがかかる気もするけど、まあいいか。<br />
<br />
今のところ、std::map&lt;string,any&gt;とstd::vector&lt;any&gt;とstringとintとdoubleが入る。つまりkeyはstringのみで、値はboost::anyをかぶせている。もちろんmapやvectorは入れ子にできる。<br />
<br />
boost::anyは何でも入れられる便利な型。<br />
参考：<a href="http://d.hatena.ne.jp/shokai/20100103/1262524286">boost::any &#8211; 橋本詳解</a><br />
<br />
<br />
必要なのはこれだけだけど、boost::any、tuple、format、foreachが必要。<br />
json_builder.h最新版は<a href="http://bitbucket.org/shokai/json-builder/src/tip/json_builder.h">bitbucketからどうぞ</a>。<br />
<br />
<a href="http://bitbucket.org/shokai/json-builder/src/c9959e7d40c2/json_builder.h">json_builder.h</a><br />
<pre class="prettyprint">
#include&nbsp;&lt;iostream&gt;<br />
#include&nbsp;&lt;string&gt;<br />
#include&nbsp;&lt;map&gt;<br />
#include&nbsp;&lt;vector&gt;<br />
#include&nbsp;&lt;boost/any.hpp&gt;<br />
#include&nbsp;&lt;boost/tuple/tuple.hpp&gt;<br />
#include&nbsp;&lt;boost/format.hpp&gt;<br />
#include&nbsp;&lt;boost/foreach.hpp&gt;<br />
<br />
using&nbsp;namespace&nbsp;std;<br />
using&nbsp;namespace&nbsp;boost;<br />
<br />
namespace&nbsp;json_builder{<br />
&nbsp;&nbsp;string&nbsp;toJson(any&nbsp;value){<br />
&nbsp;&nbsp;&nbsp;&nbsp;if(value.type()&nbsp;==&nbsp;typeid(vector&lt;any&gt;)){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string&nbsp;result_str;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vector&lt;any&gt;&nbsp;vec&nbsp;=&nbsp;any_cast&lt;vector&lt;any&gt;&nbsp;&gt;(value);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;vec.size();&nbsp;i++){<br />
	result_str&nbsp;+=&nbsp;toJson(vec[i]);<br />
	if(i&nbsp;&lt;&nbsp;vec.size()-1)&nbsp;result_str&nbsp;+=&nbsp;",";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result_str&nbsp;=&nbsp;str(format("[%s]")&nbsp;%&nbsp;result_str);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;result_str;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;else&nbsp;if(value.type()&nbsp;==&nbsp;typeid(map&lt;string,any&gt;)){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string&nbsp;result_str;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;map&lt;string,any&gt;&nbsp;m&nbsp;=&nbsp;any_cast&lt;map&lt;string,any&gt;&nbsp;&gt;(value);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string&nbsp;key;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;any&nbsp;value;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;i&nbsp;=&nbsp;0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BOOST_FOREACH(tie(key,value),&nbsp;m){<br />
	result_str&nbsp;+=&nbsp;str(format("\"%s\":%s")&nbsp;%&nbsp;key&nbsp;%&nbsp;toJson(value));<br />
	if(++i&nbsp;&lt;&nbsp;m.size())&nbsp;result_str&nbsp;+=&nbsp;",";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result_str&nbsp;=&nbsp;str(format("{%s}")&nbsp;%&nbsp;result_str);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;result_str;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;else&nbsp;if(value.type()&nbsp;==&nbsp;typeid(string)){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;str(format("\"%s\"")&nbsp;%&nbsp;any_cast&lt;string&gt;(value));<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;else&nbsp;if(value.type()&nbsp;==&nbsp;typeid(int)){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;str(format("\"%d\"")&nbsp;%&nbsp;any_cast&lt;int&gt;(value));<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;else&nbsp;if(value.type()&nbsp;==&nbsp;typeid(double)){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;str(format("\"%d\"")&nbsp;%&nbsp;any_cast&lt;double&gt;(value));<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
<br />
}<br />
</pre>
jsonは要素数不定のただの木なので、再帰でtree walkして要素の型を見て文字列に直して連結し直すだけの関数一つになった。シンプル。<br />
見ての通り、&#8221;や&#8217;や[や{はescapeしていないので、それらを含む文字列をtoJsonするとparseできないjsonができる。とりあえず今は入れる前にescapeしておいてほしい。<br />
単純にバックスラッシュつければいいだけなのかな？ → <a href="http://shokai.org/blog/archives/5046">対応した</a><br />
<br />
<br />
使ってみる。一つのstd::mapをjsonのhashとして標準出力する例<br />
<a href="http://bitbucket.org/shokai/json-builder/src/c9959e7d40c2/test/test.cpp">test.cpp</a><br />
<pre class="prettyprint">
#include&nbsp;&lt;iostream&gt;<br />
#include&nbsp;&lt;string&gt;<br />
#include&nbsp;&lt;map&gt;<br />
#include&nbsp;&lt;boost/any.hpp&gt;<br />
#include&nbsp;"../json_builder.h"<br />
<br />
int&nbsp;main(int&nbsp;argc,&nbsp;char*&nbsp;argv[]){<br />
&nbsp;&nbsp;std::map&lt;string,boost::any&gt;&nbsp;user;<br />
&nbsp;&nbsp;user["name"]&nbsp;=&nbsp;std::string(&#8220;shokai&#8221;);&nbsp;//&nbsp;string<br />
&nbsp;&nbsp;user["fullname"]&nbsp;=&nbsp;std::string(&#8220;sho&nbsp;hashimoto&#8221;);<br />
&nbsp;&nbsp;user["age"]&nbsp;=&nbsp;25;&nbsp;//&nbsp;int<br />
&nbsp;&nbsp;user["test"]&nbsp;=&nbsp;1.23;&nbsp;//&nbsp;double<br />
<br />
&nbsp;&nbsp;string&nbsp;json&nbsp;=&nbsp;json_builder::toJson(user);<br />
&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;json&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;return&nbsp;0;<br />
}<br />
</pre>
<br />
<br />
出力<br />
<pre class="prettyprint">
{"age":"25","fullname":"sho&nbsp;hashimoto","name":"shokai","test":"1.23"}<br />
</pre>
<br />
<br />
<br />
より複雑に、mapやvectorを入れ子にした例<br />
<a href="http://bitbucket.org/shokai/json-builder/src/c9959e7d40c2/test/test.cpp">test2.cpp</a><br />
<pre class="prettyprint">
#include&nbsp;&lt;iostream&gt;<br />
#include&nbsp;&lt;string&gt;<br />
#include&nbsp;&lt;map&gt;<br />
#include&nbsp;&lt;vector&gt;<br />
#include&nbsp;&lt;boost/any.hpp&gt;<br />
#include&nbsp;"../json_builder.h"<br />
<br />
using&nbsp;namespace&nbsp;json_builder;<br />
using&nbsp;namespace&nbsp;std;<br />
using&nbsp;namespace&nbsp;boost;<br />
<br />
int&nbsp;main(int&nbsp;argc,&nbsp;char*&nbsp;argv[]){<br />
&nbsp;&nbsp;map&lt;string,any&gt;&nbsp;obj;<br />
&nbsp;&nbsp;obj["test"]&nbsp;=&nbsp;string("testtest");<br />
<br />
&nbsp;&nbsp;map&lt;string,any&gt;&nbsp;user;<br />
&nbsp;&nbsp;user["name"]&nbsp;=&nbsp;string("shokai");<br />
&nbsp;&nbsp;user["fullname"]&nbsp;=&nbsp;string("sho&nbsp;hashimoto");<br />
&nbsp;&nbsp;user["age"]&nbsp;=&nbsp;25;<br />
&nbsp;&nbsp;user["test"]&nbsp;=&nbsp;6.78;<br />
&nbsp;&nbsp;obj["user"]&nbsp;=&nbsp;user;<br />
<br />
&nbsp;&nbsp;std::vector&lt;any&gt;&nbsp;vec;<br />
&nbsp;&nbsp;vec.push_back(string("aaaa"));<br />
&nbsp;&nbsp;vec.push_back(1234.56);<br />
&nbsp;&nbsp;vec.push_back(string("hello&nbsp;work"));<br />
&nbsp;&nbsp;<br />
<br />
&nbsp;&nbsp;vector&lt;any&gt;&nbsp;vec2;<br />
&nbsp;&nbsp;vec2.push_back(string("nested&nbsp;std::vector"));<br />
&nbsp;&nbsp;vec2.push_back(string("bbbbb"));<br />
&nbsp;&nbsp;vec.push_back(vec2);&nbsp;//&nbsp;std::vecotrの入れ子<br />
<br />
&nbsp;&nbsp;obj["params"]&nbsp;=&nbsp;vec;<br />
&nbsp;&nbsp;string&nbsp;json&nbsp;=&nbsp;json_builder::toJson(obj);<br />
&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;json&nbsp;&lt;&lt;&nbsp;endl;<br />
<br />
&nbsp;&nbsp;return&nbsp;0;<br />
}<br />
</pre>
<br />
<br />
出力<br />
<pre class="prettyprint">
{"params":["aaaa","1234.56","hello work",["nested std::vector","bbbbb"]],<br />
"test":"testtest","user":{"age":"25","fullname":"sho hashimoto","name":"shokai","test":"6.78"}}<br />
</pre>
<br />
<br />
出力したjsonが正しいかどうか、確認するために<a href="http://bitbucket.org/shokai/json-builder/src/c9959e7d40c2/test/json-parse.rb">rubyのjsonモジュールでrubyのオブジェクトに読み込むコード</a>をtestフォルダに置いておいた。<br />
<pre class="prettyprint">
{"params"=>["aaaa", "1234.56", "hello work", ["nested std::vector", "bbbbb"]],<br />
 "user"=><br />
  {"name"=>"shokai", "fullname"=>"sho hashimoto", "test"=>"6.78", "age"=>"25"},<br />
 "test"=>"testtest"}<br />
parse success<br />
</pre>
ちゃんと読み込めた。<br />
<br />
<br />
<br />
BOOST_FOREACHが涙が出るほど便利だった！！<br />
<ul>
  <li><a href="http://d.hatena.ne.jp/faith_and_brave/20091002/1254469396">std::mapをBoost.Foreachで回すときにキーと値を簡単に取り出す - Faith and Brave - C++で遊ぼう</a></li>
  <li><a href="http://miyano.s53.xrea.com/cgi-bin/sb/sb.cgi?cid=3-boost%20-%20Foreach">Pasture | C++::boost - Foreach</a></li>
</ul>
</p>
]]></content:encoded>
			<wfw:commentRss>http://shokai.org/blog/archives/5021/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>OpenCVで画像のサイズを求めるgearman workerを作って、Rubyから呼ぶ</title>
		<link>http://shokai.org/blog/archives/4987</link>
		<comments>http://shokai.org/blog/archives/4987#comments</comments>
		<pubDate>Sun, 03 Jan 2010 03:29:08 +0000</pubDate>
		<dc:creator>shokai</dc:creator>
				<category><![CDATA[未分類]]></category>
		<category><![CDATA[boost]]></category>
		<category><![CDATA[cpp]]></category>
		<category><![CDATA[gearman]]></category>
		<category><![CDATA[OpenCV]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://shokai.org/blog/?p=4987</guid>
		<description><![CDATA[ファイル名を渡すと画像サイズを返すgearman workerを作った。 {"width":1600,&#160;"height":1200} という風にJSON風に値を返す。 以前画像のだいたいの色を求めるgearma [...]]]></description>
			<content:encoded><![CDATA[<p>
ファイル名を渡すと画像サイズを返すgearman workerを作った。<br />
<pre class="prettyprint">
{"width":1600,&nbsp;"height":1200}<br />
</pre>
という風にJSON風に値を返す。<br />
<br />
<br />
以前<a href="http://shokai.org/blog/archives/4966">画像のだいたいの色を求めるgearman worker</a>を作ってたんだけど、C++で書いたworker側を単独で動作テストする事ができなくてどうしようか悩んだ。テスト用のclientと同時に作らなければならなくて、どちらにバグがあるのか切り分けるのが面倒だった。<br />
<br />
そこで、workerの起動時に<br />
<pre>
./imgsizeWorker --test "/path/to/imagefile.jpg"<br />
</pre>
という風にgearman clientから来る引数と同じ形式で渡すと、単体のプログラムとしても動作チェックできるようにした。<br />
<br />
<br />
こうすると普通にworkerとして起動する。<br />
<pre>
./imgsizeWorker -s localhost -p 7003<br />
</pre>
これはboost::program_optionsでやると便利だった。<br />
今後はこのコードを雛形にすればC++とOpenCVで高速に画像解析して、gearmand経由でスクリプト言語から呼び出しまくれる。<br />
<br />
<br />
■使ったライブラリ<br />
boostライブラリを中心にいろいろ使った。<br />
<br />
opencv1.0は画像サイズを取得する為だけに使った。GUIを使って無いのにcvLoadImage()のためだけにhighgui.hを読み込んでいる。<br />
<br />
boost::tupleとboost::tieを使うと返り値を複数返す関数が作れるので、画像の情報を返すのに便利。そういえばRubyでは普通に使ってたけどC#3.5やAS3.0ではタプル無かったな。欲しい。ASはArrayに何でも入れやすいからいいか。<br />
<a href="http://d.hatena.ne.jp/shokai/20091231/1262248887">boost::tupleで多値を受け取る &#8211; 橋本詳解</a><br />
<a href="http://d.hatena.ne.jp/shokai/20091231/1262249708">boost::tieでtupleを展開 &#8211; 橋本詳解</a><br />
<br />
boost::formatでprintf風にstd::stringをフォーマット。<br />
boost::program_optionsで引数をparseする。<br />
<a href="http://d.hatena.ne.jp/shokai/20090729/1248884601">boost::program_optionsでコマンドライン引数を読む &#8211; 橋本詳解</a><br />
<br />
rubyはxing-gearman-rubyを使った。<br />
<a href="http://shokai.org/blog/archives/4919">橋本商会  Cでgearman workerを書いてRubyのclientから呼び出す</a><br />
<br />
<br />
■プログラム<br />
libgearmanのgearman_worker_add_serverはIPアドレスを渡さないとならないので、一応ホスト名を解決するようにした。<br />
<br />
<a href="http://shokai.org/projects/gearman-study/index.cgi/file/3a774a6d883d/imgsize/imgsizeWorker.cpp">imgsizeWorker.cpp</a><br />
<pre class="prettyprint">
//&nbsp;画像サイズを返すgearman&nbsp;worker<br />
#include&nbsp;&lt;stdio.h&gt;<br />
#include&nbsp;&lt;stdlib.h&gt;<br />
#include&nbsp;&lt;unistd.h&gt;<br />
#include&nbsp;&lt;string&gt;<br />
#include&nbsp;&lt;iostream&gt;<br />
#include&nbsp;&lt;cv.h&gt;<br />
#include&nbsp;&lt;highgui.h&gt;<br />
#include&nbsp;&lt;boost/program_options.hpp&gt;<br />
#include&nbsp;&lt;boost/regex.hpp&gt;<br />
#include&nbsp;&lt;boost/format.hpp&gt;<br />
#include&nbsp;&lt;boost/tuple/tuple.hpp&gt;<br />
#include&nbsp;&lt;boost/tuple/tuple_io.hpp&gt;<br />
#include&nbsp;&lt;libgearman/gearman.h&gt;<br />
<br />
using&nbsp;namespace&nbsp;boost;<br />
using&nbsp;namespace&nbsp;std;<br />
<br />
tuple&lt;int,&nbsp;int&gt;&nbsp;get_size(string&nbsp;fileName);&nbsp;//&nbsp;画像のwidth,heightを返す<br />
string&nbsp;imgsize(string&nbsp;fileName);&nbsp;//&nbsp;gearman&nbsp;workerとしてclientに返すstringに整形する<br />
void&nbsp;*job_imgsize(gearman_job_st&nbsp;*job,&nbsp;void&nbsp;*cb_arg,&nbsp;size_t&nbsp;*result_size,&nbsp;gearman_return_t&nbsp;*ret_ptr);<br />
<br />
int&nbsp;main(int&nbsp;argc,&nbsp;char*&nbsp;argv[])&nbsp;{<br />
&nbsp;&nbsp;program_options::options_description&nbsp;opts("options");<br />
&nbsp;&nbsp;opts.add_options()<br />
&nbsp;&nbsp;&nbsp;&nbsp;("help,h",&nbsp;"helpを表示")<br />
&nbsp;&nbsp;&nbsp;&nbsp;("server,s",&nbsp;program_options::value&lt;string&gt;(),&nbsp;"gearmanサーバーのアドレス")<br />
&nbsp;&nbsp;&nbsp;&nbsp;("port,p",&nbsp;program_options::value&lt;int&gt;(),&nbsp;"gearmanサーバーのport番号")<br />
&nbsp;&nbsp;&nbsp;&nbsp;("test,t",&nbsp;program_options::value&lt;string&gt;(),&nbsp;"gearman&nbsp;worker単体テスト用query");<br />
&nbsp;&nbsp;program_options::variables_map&nbsp;argmap;<br />
&nbsp;&nbsp;program_options::store(parse_command_line(argc,&nbsp;argv,&nbsp;opts),&nbsp;argmap);<br />
&nbsp;&nbsp;program_options::notify(argmap);<br />
<br />
&nbsp;&nbsp;if(!argmap.count("help")){<br />
&nbsp;&nbsp;&nbsp;&nbsp;if(argmap.count("test")){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;"---test---"&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string&nbsp;gearman_param&nbsp;=&nbsp;argmap["test"].as&lt;string&gt;();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;imgsize(gearman_param);&nbsp;//&nbsp;単体でworkerとしてのテスト<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}else&nbsp;if(argmap.count("server")&nbsp;&amp;&amp;&nbsp;argmap.count("port")){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gearman_worker_st&nbsp;worker;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gearman_worker_create(&amp;worker);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string&nbsp;g_server&nbsp;=&nbsp;argmap["server"].as&lt;string&gt;();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;g_port&nbsp;=&nbsp;argmap["port"].as&lt;int&gt;();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;hostent&nbsp;*g_host&nbsp;=&nbsp;gethostbyname((char*)g_server.c_str());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string&nbsp;g_server_addr&nbsp;=&nbsp;str(format("%d.%d.%d.%d")&nbsp;%<br />
				&nbsp;(uint)(uchar)g_host-&gt;h_addr[0]&nbsp;%<br />
				&nbsp;(uint)(uchar)g_host-&gt;h_addr[1]&nbsp;%<br />
				&nbsp;(uint)(uchar)g_host-&gt;h_addr[2]&nbsp;%<br />
				&nbsp;(uint)(uchar)g_host-&gt;h_addr[3]);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gearman_worker_add_server(&amp;worker,&nbsp;g_server_addr.c_str(),&nbsp;g_port);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gearman_worker_add_function(&amp;worker,&nbsp;"img_size",&nbsp;0,&nbsp;job_imgsize,&nbsp;NULL);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;str(format("---start&nbsp;worker&nbsp;(%s:%d)---")&nbsp;%<br />
		&nbsp;&nbsp;g_server_addr&nbsp;%&nbsp;g_port)&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while(true)&nbsp;gearman_worker_work(&amp;worker);&nbsp;//&nbsp;workerとして待機<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;cerr&nbsp;&lt;&lt;&nbsp;"server,portが必要です"&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;cerr&nbsp;&lt;&lt;&nbsp;opts&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;return&nbsp;1;<br />
&nbsp;&nbsp;<br />
}<br />
<br />
//&nbsp;opencvで画像サイズを取得<br />
tuple&lt;int,&nbsp;int&gt;&nbsp;get_size(string&nbsp;fileName){<br />
&nbsp;&nbsp;IplImage&nbsp;*img&nbsp;=&nbsp;cvLoadImage(fileName.c_str());<br />
&nbsp;&nbsp;if(!img){<br />
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;make_tuple(-1,&nbsp;-1);<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;else{<br />
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;make_tuple(img-&gt;width,&nbsp;img-&gt;height);<br />
&nbsp;&nbsp;&nbsp;&nbsp;cvReleaseImage(&amp;img);<br />
&nbsp;&nbsp;}<br />
}<br />
<br />
//&nbsp;画像サイズを取得してgearman&nbsp;serverに返すstringに整形する<br />
string&nbsp;imgsize(string&nbsp;fileName){<br />
&nbsp;&nbsp;string&nbsp;result_str&nbsp;=&nbsp;"";<br />
&nbsp;&nbsp;int&nbsp;width,&nbsp;height;<br />
&nbsp;&nbsp;tie(width,&nbsp;height)&nbsp;=&nbsp;get_size(fileName);<br />
&nbsp;&nbsp;if(width&nbsp;&gt;&nbsp;0&nbsp;&amp;&amp;&nbsp;height&nbsp;&gt;&nbsp;0){<br />
&nbsp;&nbsp;&nbsp;&nbsp;result_str&nbsp;+=&nbsp;str(format("{\"width\":%d,&nbsp;\"height\":%d}")	<br />
		&nbsp;&nbsp;&nbsp;&nbsp;%&nbsp;width&nbsp;%&nbsp;height);<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;else{<br />
&nbsp;&nbsp;&nbsp;&nbsp;result_str&nbsp;=&nbsp;"error&nbsp;:&nbsp;image&nbsp;load&nbsp;error";<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;fileName&nbsp;&lt;&lt;&nbsp;"&nbsp;=&gt;&nbsp;"&nbsp;&lt;&lt;&nbsp;result_str&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;return&nbsp;result_str;<br />
}<br />
<br />
//&nbsp;gearman&nbsp;worker&nbsp;job<br />
void&nbsp;*job_imgsize(gearman_job_st&nbsp;*job,&nbsp;void&nbsp;*cb_arg,&nbsp;size_t&nbsp;*result_size,&nbsp;gearman_return_t&nbsp;*ret_ptr){<br />
&nbsp;&nbsp;string&nbsp;fileName&nbsp;=&nbsp;(char*)gearman_job_workload(job);<br />
&nbsp;&nbsp;string&nbsp;result_str&nbsp;=&nbsp;imgsize(fileName);<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;char&nbsp;*result&nbsp;=&nbsp;(char*)strdup(result_str.c_str());<br />
&nbsp;&nbsp;*result_size&nbsp;=&nbsp;result_str.size();<br />
&nbsp;&nbsp;*ret_ptr&nbsp;=&nbsp;GEARMAN_SUCCESS;<br />
&nbsp;&nbsp;return&nbsp;result;<br />
}<br />
</pre>
<br />
<br />
<a href="http://shokai.org/projects/gearman-study/index.cgi/file/3a774a6d883d/imgsize/Makefile">Makefile</a><br />
<pre class="prettyprint">
#&nbsp;Mac用Makefile<br />
SRC&nbsp;=&nbsp;imgsizeWorker.cpp<br />
DST&nbsp;=&nbsp;imgsizeWorker<br />
<br />
prefix=/opt/local<br />
INCPATH=$(prefix)/include<br />
LIBPATH=$(prefix)/lib<br />
<br />
CV_LIBS=&nbsp;-lcv&nbsp;-lcvaux&nbsp;-lcxcore&nbsp;-lhighgui<br />
BOOST_LIBS=&nbsp;$(LIBPATH)/libboost_program_options-mt.a<br />
<br />
GEAR_INCPATH=/usr/local/include<br />
GEAR_LIBPATH=/usr/local/lib<br />
GEAR_LIBS=$(GEAR_LIBPATH)/libgearman.a<br />
<br />
all:<br />
	g++&nbsp;-O&nbsp;$(SRC)&nbsp;-o&nbsp;$(DST)&nbsp;-I$(INCPATH)/opencv&nbsp;-L.&nbsp;-L$(LIBPATH)&nbsp;$(CV_LIBS)&nbsp;-I$(INCPATH)/boost&nbsp;$(BOOST_LIBS)&nbsp;-I$(GEAR_INCPATH)/libgearman&nbsp;-L.&nbsp;-L$(GEAR_LIBPATH)&nbsp;$(GEAR_LIBS)<br />
</pre>
<br />
<br />
client側。workerからの返り値の先頭にerrorが書いていなかったらJSONとしてparseする<br />
<a href="http://shokai.org/projects/gearman-study/index.cgi/file/3a774a6d883d/imgsize/testclient.rb">testclient.rb</a><br />
<pre class="prettyprint">
#!/usr/bin/env&nbsp;ruby<br />
#&nbsp;-*-&nbsp;coding:&nbsp;utf-8&nbsp;-*-<br />
require&nbsp;'rubygems'<br />
require&nbsp;'gearman'<br />
require&nbsp;'json'<br />
<br />
if&nbsp;ARGV.size&nbsp;&lt;&nbsp;1<br />
&nbsp;&nbsp;puts&nbsp;'画像へのパスが必要'<br />
&nbsp;&nbsp;puts&nbsp;'ruby&nbsp;testclient.rb&nbsp;~/path/to/images/*.png'<br />
&nbsp;&nbsp;exit&nbsp;1<br />
end<br />
<br />
c&nbsp;=&nbsp;Gearman::Client.new(['localhost:7003'])<br />
taskset&nbsp;=&nbsp;Gearman::TaskSet.new(c)<br />
<br />
ARGV.sort{|a,b|&nbsp;a.split(/\//).last.to_i&nbsp;&lt;=&gt;&nbsp;b.split(/\//).last.to_i}.each{|name|<br />
&nbsp;&nbsp;puts&nbsp;"add&nbsp;task&nbsp;#{name}"<br />
&nbsp;&nbsp;task&nbsp;=&nbsp;Gearman::Task.new("img_size",&nbsp;name+"\0")<br />
&nbsp;&nbsp;task.on_complete{|result|<br />
&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;!(result&nbsp;=~&nbsp;/^error/)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print&nbsp;"return:&nbsp;#{name}&nbsp;=&gt;&nbsp;"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p&nbsp;JSON.parse(result)&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts&nbsp;"return:&nbsp;#{name}&nbsp;=&gt;&nbsp;#{result}"<br />
&nbsp;&nbsp;&nbsp;&nbsp;end<br />
<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;taskset.add_task(task)<br />
}<br />
taskset.wait(100)&nbsp;#&nbsp;wait&nbsp;100(sec)<br />
</pre>
<br />
<br />
&amp;をつけていくつかworkerを起動する<br />
<pre class="prettyprint">
./imgsizeWorker -s localhost -p 7003&#038;<br />
./imgsizeWorker -s localhost -p 7003&#038;<br />
</pre>
<br />
<br />
clientからtask登録。フォルダ内のjpgファイルを全部登録する<br />
<pre class="prettyprint">
ruby testclient.rb ~/Pictures/selected/*.jpg<br />
</pre>
サイズが返ってくる<br />
<pre class="prettyprint">
add task /Users/sho/Pictures/selected/a66dab3a.jpg<br />
add task /Users/sho/Pictures/selected/3ed6f38e.jpg<br />
add task /Users/sho/Pictures/selected/77ab53f0.jpg<br />
add task /Users/sho/Pictures/selected/889bd644.jpg<br />
add task /Users/sho/Pictures/selected/73177294.jpg<br />
return: /Users/sho/Pictures/selected/a66dab3a.jpg => {"height"=>1200, "width"=>1600}<br />
return: /Users/sho/Pictures/selected/3ed6f38e.jpg => {"height"=>1200, "width"=>1600}<br />
return: /Users/sho/Pictures/selected/77ab53f0.jpg => {"height"=>1200, "width"=>1600}<br />
return: /Users/sho/Pictures/selected/889bd644.jpg => {"height"=>800, "width"=>1280}<br />
return: /Users/sho/Pictures/selected/73177294.jpg => {"height"=>1200, "width"=>1600}<br />
</pre>
</p>
]]></content:encoded>
			<wfw:commentRss>http://shokai.org/blog/archives/4987/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>OpenCVで画像のだいたいの色を求めるgearman workerを作って、Rubyから呼び出す</title>
		<link>http://shokai.org/blog/archives/4966</link>
		<comments>http://shokai.org/blog/archives/4966#comments</comments>
		<pubDate>Thu, 24 Dec 2009 12:14:37 +0000</pubDate>
		<dc:creator>shokai</dc:creator>
				<category><![CDATA[未分類]]></category>
		<category><![CDATA[cpp]]></category>
		<category><![CDATA[gearman]]></category>
		<category><![CDATA[OpenCV]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://shokai.org/blog/?p=4966</guid>
		<description><![CDATA[画像のだいたいの色を求めるをgearman worker化した。 画像ファイル名をテキストで投げると {width: 136, height: 147, r: 249, g: 234, b:230} という形式で画像のサ [...]]]></description>
			<content:encoded><![CDATA[<p>
<a href="http://shokai.org/blog/archives/4961">画像のだいたいの色を求める</a>をgearman worker化した。<br />
画像ファイル名をテキストで投げると<br />
<pre class="prettyprint">
{width: 136, height: 147, r: 249, g: 234, b:230}<br />
</pre>
という形式で画像のサイズとだいたいの色を返す。<br />
<br />
<br />
Cでworkerを書いてRubyで呼び出す方法は<a href="http://shokai.org/blog/archives/4919">橋本商会  Cでgearman workerを書いてRubyのclientから呼び出す</a>に書いたとおり。<br />
これでOpenCVの解析をgearman workerとして複数並列に動かしてそれをRubyから使いまくるというのができるワハハ<br />
<br />
<br />
C++なのは<a href="http://d.hatena.ne.jp/shokai/20091219/1261195886">boost::regex_split</a>でも使って引数を受け取ろうかと思ってたんだけど、そもそもファイル名だけしか受け取らなかったから意味がなかった。<br />
<a href="http://shokai.org/projects/gearman-study/index.cgi/file/f306280ee764/imgcolor/imgcolorWorker.cpp">imgcolorWorker.cpp</a><br />
<pre class="prettyprint">
//&nbsp;画像のだいたいの色とサイズを返すgearman&nbsp;worker<br />
#include&nbsp;&lt;stdio.h&gt;<br />
#include&nbsp;&lt;stdlib.h&gt;<br />
#include&nbsp;&lt;unistd.h&gt;<br />
#include&nbsp;&lt;string&gt;<br />
#include&nbsp;&lt;iostream&gt;<br />
#include&nbsp;&lt;cv.h&gt;<br />
#include&nbsp;&lt;highgui.h&gt;<br />
#include&nbsp;&lt;boost/regex.hpp&gt;<br />
#include&nbsp;&lt;boost/format.hpp&gt;<br />
#include&nbsp;&lt;libgearman/gearman.h&gt;<br />
<br />
using&nbsp;namespace&nbsp;boost;<br />
using&nbsp;namespace&nbsp;std;<br />
IplImage&nbsp;*img,&nbsp;*img1px,&nbsp;*imgR,&nbsp;*imgG,&nbsp;*imgB;<br />
<br />
void&nbsp;*job_imgcolor(gearman_job_st&nbsp;*job,&nbsp;void&nbsp;*cb_arg,&nbsp;size_t&nbsp;*result_size,&nbsp;gearman_return_t&nbsp;*ret_ptr);<br />
<br />
int&nbsp;main(int&nbsp;argc,&nbsp;char*&nbsp;argv[])&nbsp;{<br />
&nbsp;&nbsp;img1px&nbsp;=&nbsp;cvCreateImage(cvSize(1,1),&nbsp;IPL_DEPTH_8U,&nbsp;3);<br />
&nbsp;&nbsp;imgR&nbsp;=&nbsp;cvCreateImage(cvSize(1,1),&nbsp;IPL_DEPTH_8U,&nbsp;1);<br />
&nbsp;&nbsp;imgG&nbsp;=&nbsp;cvCreateImage(cvSize(1,1),&nbsp;IPL_DEPTH_8U,&nbsp;1);<br />
&nbsp;&nbsp;imgB&nbsp;=&nbsp;cvCreateImage(cvSize(1,1),&nbsp;IPL_DEPTH_8U,&nbsp;1);<br />
<br />
&nbsp;&nbsp;gearman_worker_st&nbsp;worker;<br />
&nbsp;&nbsp;gearman_worker_create(&amp;worker);<br />
&nbsp;&nbsp;gearman_worker_add_server(&amp;worker,&nbsp;"127.0.0.1",&nbsp;7003);<br />
&nbsp;&nbsp;gearman_worker_add_function(&amp;worker,&nbsp;"img_color",&nbsp;0,&nbsp;job_imgcolor,&nbsp;NULL);<br />
<br />
&nbsp;&nbsp;while(true)&nbsp;gearman_worker_work(&amp;worker);<br />
&nbsp;&nbsp;return&nbsp;0;<br />
}<br />
<br />
void&nbsp;*job_imgcolor(gearman_job_st&nbsp;*job,&nbsp;void&nbsp;*cb_arg,&nbsp;size_t&nbsp;*result_size,&nbsp;gearman_return_t&nbsp;*ret_ptr){<br />
&nbsp;&nbsp;string&nbsp;fileName&nbsp;=&nbsp;(char*)gearman_job_workload(job);<br />
&nbsp;&nbsp;string&nbsp;result_str&nbsp;=&nbsp;"";<br />
&nbsp;&nbsp;IplImage&nbsp;*img&nbsp;=&nbsp;cvLoadImage(fileName.c_str());<br />
&nbsp;&nbsp;if(!img){<br />
&nbsp;&nbsp;&nbsp;&nbsp;result_str&nbsp;+=&nbsp;"{error:&nbsp;image&nbsp;load&nbsp;error}";<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;else{<br />
&nbsp;&nbsp;&nbsp;&nbsp;cvResize(img,&nbsp;img1px,&nbsp;CV_INTER_CUBIC);<br />
&nbsp;&nbsp;&nbsp;&nbsp;cvSplit(img1px,&nbsp;imgB,&nbsp;imgG,&nbsp;imgR,&nbsp;NULL);<br />
&nbsp;&nbsp;&nbsp;&nbsp;result_str&nbsp;+=&nbsp;str(format("{width:&nbsp;%d,&nbsp;height:&nbsp;%d,&nbsp;r:&nbsp;%d,&nbsp;g:&nbsp;%d,&nbsp;b:%d}")	<br />
		&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;%&nbsp;img-&gt;width&nbsp;%&nbsp;img-&gt;height&nbsp;%<br />
		&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(uint)(uchar)imgR-&gt;imageDataOrigin[0]&nbsp;%<br />
		&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(uint)(uchar)imgG-&gt;imageDataOrigin[0]&nbsp;%<br />
		&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(uint)(uchar)imgB-&gt;imageDataOrigin[0]);<br />
&nbsp;&nbsp;&nbsp;&nbsp;cvReleaseImage(&amp;img);<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;fileName&nbsp;&lt;&lt;&nbsp;"&nbsp;=&gt;&nbsp;"&nbsp;&lt;&lt;&nbsp;result_str&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;char&nbsp;*result&nbsp;=&nbsp;(char*)strdup(result_str.c_str());<br />
&nbsp;&nbsp;*result_size&nbsp;=&nbsp;result_str.size();<br />
&nbsp;&nbsp;*ret_ptr&nbsp;=&nbsp;GEARMAN_SUCCESS;<br />
&nbsp;&nbsp;return&nbsp;result;<br />
}<br />
</pre>
最後の*result_sizeに返り値のサイズを入れておかないと、client側には壊れた文字列が来る。<br />
boost::formatはprintfのフォーマットと違ってunsigned charを数値としてそのまま使えなかったので2回キャストした。<br />
<br />
<br />
<a href="http://shokai.org/projects/gearman-study/index.cgi/file/f306280ee764/imgcolor/Makefile">Makefile</a><br />
<pre class="prettyprint">
#&nbsp;Mac用Makefile<br />
SRC&nbsp;=&nbsp;imgcolorWorker.cpp<br />
DST&nbsp;=&nbsp;imgcolorWorker<br />
<br />
prefix=/opt/local<br />
INCPATH=$(prefix)/include<br />
LIBPATH=$(prefix)/lib<br />
<br />
CV_LIBS=&nbsp;-lcv&nbsp;-lcvaux&nbsp;-lcxcore&nbsp;-lhighgui<br />
BOOST_LIBS=&nbsp;$(LIBPATH)/libboost_program_options-mt.a&nbsp;$(LIBPATH)/libboost_regex-mt.a<br />
<br />
GEAR_INCPATH=/usr/local/include<br />
GEAR_LIBPATH=/usr/local/lib<br />
GEAR_LIBS=$(GEAR_LIBPATH)/libgearman.a<br />
<br />
all:<br />
	g++&nbsp;-O&nbsp;$(SRC)&nbsp;-o&nbsp;$(DST)&nbsp;-I$(INCPATH)/opencv&nbsp;-L.&nbsp;-L$(LIBPATH)&nbsp;$(CV_LIBS)&nbsp;-I$(INCPATH)/boost&nbsp;$(BOOST_LIBS)&nbsp;-I$(GEAR_INCPATH)/libgearman&nbsp;-L.&nbsp;-L$(GEAR_LIBPATH)&nbsp;$(GEAR_LIBS)<br />
</pre>
<br />
<br />
<br />
gearman client。ワイルドカードで指定して画像ファイルをひとつずつtask登録する<br />
<a href="http://shokai.org/projects/gearman-study/index.cgi/file/f306280ee764/imgcolor/imgcolorClient.rb">imgcolorClient.rb</a><br />
<pre class="prettyprint">
#!/usr/bin/env&nbsp;ruby<br />
#&nbsp;-*-&nbsp;coding:&nbsp;utf-8&nbsp;-*-<br />
require&nbsp;'rubygems'<br />
require&nbsp;'gearman'<br />
require&nbsp;'json'<br />
<br />
if&nbsp;ARGV.size&nbsp;&lt;&nbsp;1<br />
&nbsp;&nbsp;puts&nbsp;'画像へのパスが必要'<br />
&nbsp;&nbsp;puts&nbsp;'ruby&nbsp;imgcolorClient.rb&nbsp;~/path/to/images/*.png'<br />
&nbsp;&nbsp;exit&nbsp;1<br />
end<br />
<br />
c&nbsp;=&nbsp;Gearman::Client.new(['localhost:7003'])<br />
taskset&nbsp;=&nbsp;Gearman::TaskSet.new(c)<br />
<br />
ARGV.sort{|a,b|&nbsp;a.split(/\//).last.to_i&nbsp;&lt;=&gt;&nbsp;b.split(/\//).last.to_i}.each{|name|<br />
&nbsp;&nbsp;puts&nbsp;"add&nbsp;task&nbsp;#{name}"<br />
&nbsp;&nbsp;task&nbsp;=&nbsp;Gearman::Task.new("img_color",&nbsp;name+"\0")<br />
&nbsp;&nbsp;task.on_complete{|result|<br />
&nbsp;&nbsp;&nbsp;&nbsp;puts&nbsp;"return:&nbsp;#{name}&nbsp;=&gt;&nbsp;#{result}"<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;taskset.add_task(task)<br />
}<br />
taskset.wait(100)&nbsp;#&nbsp;wait&nbsp;100(sec)<br />
</pre>
task登録する時に、argmentの末尾に&#8221;\0&#8243;を付けておかないとworker側で受け取った時におかしくなる事がある。<br />
<br />
<br />
<br />
workerを2つ起動しておいて、画像をいくつか投げてみる<br />
<pre class="prettyprint">
make<br />
./imgcolorWorker&#038;<br />
./imgcolorWorker&#038;<br />
ruby imgcolorClient.rb ~/Pictures/test/*<br />
</pre>
<br />
<br />
workerが2つあるので、結果は順番には返ってこない。<br />
<pre class="prettyprint">
add task /Users/sho/Pictures/test/実装力不全.gif<br />
add task /Users/sho/Pictures/test/そうだ旧都へ行こう.jpg<br />
add task /Users/sho/Pictures/test/zanmai.jpg<br />
add task /Users/sho/Pictures/test/wiring.jpg<br />
return: /Users/sho/Pictures/test/実装力不全.gif => {error: image load error}<br />
add task /Users/sho/Pictures/test/ed93d6de.jpg<br />
add task /Users/sho/Pictures/test/f9286cff.jpg<br />
add task /Users/sho/Pictures/test/fae75abe.jpg<br />
add task /Users/sho/Pictures/test/img278_s201.jpg<br />
add task /Users/sho/Pictures/test/la-metro.jpg<br />
add task /Users/sho/Pictures/test/wiring-s.jpg<br />
add task /Users/sho/Pictures/test/org19361.jpg<br />
add task /Users/sho/Pictures/test/org19364.jpg<br />
add task /Users/sho/Pictures/test/org19375.jpg<br />
add task /Users/sho/Pictures/test/label.png<br />
add task /Users/sho/Pictures/test/246.png<br />
add task /Users/sho/Pictures/test/247.png<br />
add task /Users/sho/Pictures/test/248.png<br />
add task /Users/sho/Pictures/test/249.png<br />
return: /Users/sho/Pictures/test/zanmai.jpg => {width: 1024, height: 768, r: 22, g: 11, b:7}<br />
return: /Users/sho/Pictures/test/そうだ旧都へ行こう.jpg => {width: 1440, height: 1046, r: 21, g: 21, b:20}<br />
return: /Users/sho/Pictures/test/ed93d6de.jpg => {width: 1920, height: 1200, r: 45, g: 33, b:26}<br />
return: /Users/sho/Pictures/test/wiring.jpg => {width: 2106, height: 1584, r: 26, g: 24, b:31}<br />
return: /Users/sho/Pictures/test/f9286cff.jpg => {width: 1920, height: 1200, r: 88, g: 80, b:72}<br />
return: /Users/sho/Pictures/test/fae75abe.jpg => {width: 1920, height: 1200, r: 21, g: 15, b:22}<br />
return: /Users/sho/Pictures/test/img278_s201.jpg => {width: 1024, height: 768, r: 21, g: 92, b:107}<br />
return: /Users/sho/Pictures/test/la-metro.jpg => {width: 1024, height: 768, r: 128, g: 118, b:111}<br />
return: /Users/sho/Pictures/test/wiring-s.jpg => {width: 1024, height: 770, r: 28, g: 24, b:32}<br />
return: /Users/sho/Pictures/test/org19361.jpg => {width: 1280, height: 800, r: 0, g: 0, b:0}<br />
return: /Users/sho/Pictures/test/org19375.jpg => {width: 1024, height: 633, r: 235, g: 235, b:235}<br />
return: /Users/sho/Pictures/test/org19364.jpg => {width: 1024, height: 681, r: 217, g: 217, b:217}<br />
return: /Users/sho/Pictures/test/label.png => {width: 100, height: 80, r: 255, g: 255, b:255}<br />
return: /Users/sho/Pictures/test/247.png => {width: 140, height: 151, r: 246, g: 235, b:233}<br />
return: /Users/sho/Pictures/test/248.png => {width: 138, height: 149, r: 247, g: 234, b:232}<br />
return: /Users/sho/Pictures/test/246.png => {width: 141, height: 153, r: 246, g: 237, b:236}<br />
return: /Users/sho/Pictures/test/249.png => {width: 136, height: 147, r: 249, g: 234, b:230}<br />
</pre>
<br />
<br />
ちなみにCore2 DuoのMacbookProなので、workerを1つよりも2つ起動していた方が1.7倍ぐらい速かった。<br />
<br />
引数を複数渡したい時は、適当に渡してworker側で<a href="http://d.hatena.ne.jp/shokai/20091219/1261195886">boost::regex_split</a>でも使うか、getoptやboost::program_optionsでparseできるようにして渡せばいいかな<br />
client側での受け取りはRubyだからどうにでもできる。<br />
<br />
workerでエラーが起きた時の例外処理の出し方がよくわからない。ret_ptrにGEARMAN_SUCCESS以外を入れて返すべきなのか？<br />
</p>
]]></content:encoded>
			<wfw:commentRss>http://shokai.org/blog/archives/4966/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>cvCalcOpticalFlowBM</title>
		<link>http://shokai.org/blog/archives/4820</link>
		<comments>http://shokai.org/blog/archives/4820#comments</comments>
		<pubDate>Mon, 05 Oct 2009 14:47:47 +0000</pubDate>
		<dc:creator>shokai</dc:creator>
				<category><![CDATA[未分類]]></category>
		<category><![CDATA[cpp]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[OpenCV]]></category>
		<category><![CDATA[OpticalFlow]]></category>
		<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://shokai.org/blog/?p=4820</guid>
		<description><![CDATA[オプティカルフローのサンプルコードをちょっといじって、カメラからの入力を表示してみた。 ブロックマッチングによるオプティカルフローを使った。 できたソースコード、Makefile image.cpp #include &#038; [...]]]></description>
			<content:encoded><![CDATA[<p>
<a href="http://opencv.jp/sample/optical_flow.html">オプティカルフローのサンプルコード</a>をちょっといじって、カメラからの入力を表示してみた。<br />
ブロックマッチングによるオプティカルフローを使った。<br />
<br />
<a href="http://shokai.org/projects/opencv-study-mac/index.cgi/file/f5ca4d2805f2/opticalflow/">できたソースコード、Makefile</a><br />
<br />
<br />
<a href="http://www.flickr.com/photos/shokai/3983395773/" title="narupeko opticalflow by shokai, on Flickr"><img src="http://farm3.static.flickr.com/2453/3983395773_1fa7ea72e0.jpg" width="500" height="313" alt="narupeko opticalflow" /></a><a href="http://www.flickr.com/photos/shokai/3983397263/" title="narupeko opticalflow by shokai, on Flickr"><img src="http://farm3.static.flickr.com/2509/3983397263_b5988276e9.jpg" width="500" height="313" alt="narupeko opticalflow" /></a><a href="http://www.flickr.com/photos/shokai/3983390155/" title="narupeko opticalflow by shokai, on Flickr"><img src="http://farm4.static.flickr.com/3420/3983390155_0bb4e18b66.jpg" width="500" height="316" alt="narupeko opticalflow" /></a><a href="http://www.flickr.com/photos/shokai/3983391689/" title="narupeko opticalflow by shokai, on Flickr"><img src="http://farm3.static.flickr.com/2495/3983391689_85d719308a.jpg" width="500" height="313" alt="narupeko opticalflow" /></a><a href="http://www.flickr.com/photos/shokai/3984154476/" title="narupeko opticalflow by shokai, on Flickr"><img src="http://farm3.static.flickr.com/2432/3984154476_5652d6bfde.jpg" width="500" height="313" alt="narupeko opticalflow" /></a><br />
<br />
<a href="http://shokai.org/projects/opencv-study-mac/index.cgi/file/f5ca4d2805f2/opticalflow/image.cpp">image.cpp</a><br />
<pre class="prettyprint">
#include &lt;cv.h&gt;<br />
#include &lt;highgui.h&gt;<br />
#include &lt;cxcore.h&gt;<br />
#include &lt;ctype.h&gt;<br />
#include &lt;stdio.h&gt;<br />
#include &lt;iostream&gt;<br />
#include &lt;boost/format.hpp&gt;<br />
<br />
using namespace std;<br />
using namespace boost;<br />
<br />
void detect_flow(IplImage *img, IplImage *img_p, IplImage *dst);<br />
<br />
int main(int argc, char* argv[]) {<br />
  IplImage *img = NULL;<br />
  CvCapture *capture = NULL;<br />
  capture = cvCreateCameraCapture(0);<br />
  //capture = cvCaptureFromAVI("test.avi");<br />
  if(capture == NULL){<br />
    cerr &lt;&lt; "capture device not found!!" &lt;&lt; endl;<br />
    return -1;<br />
  }<br />
<br />
  CvSize size = cvSize(240, 180);<br />
  IplImage *img_resized = cvCreateImage(size, IPL_DEPTH_8U, 3);<br />
  IplImage *img_gray = cvCreateImage(size, IPL_DEPTH_8U, 1);<br />
  IplImage *img_gray_p = cvCreateImage(size, IPL_DEPTH_8U, 1);<br />
  IplImage *img_dst = cvCreateImage(size, IPL_DEPTH_8U, 3);<br />
<br />
  char winNameCapture[] = "Capture";<br />
  cvNamedWindow(winNameCapture, CV_WINDOW_AUTOSIZE);<br />
  <br />
  while (1) {<br />
    img = cvQueryFrame(capture);<br />
    cvResize(img, img_resized);<br />
    cvCvtColor(img_resized, img_gray, CV_BGR2GRAY);<br />
    cvCopy(img_resized, img_dst);<br />
    detect_flow(img_gray, img_gray_p, img_dst);<br />
    cvShowImage(winNameCapture, img_dst);<br />
    cvCopy(img_gray, img_gray_p);<br />
    if (cvWaitKey(10) == 'q') break;<br />
  }<br />
  <br />
  cvReleaseCapture(&amp;capture);<br />
  cvDestroyWindow(winNameCapture);<br />
  <br />
  return 0;<br />
}<br />
<br />
void detect_flow(IplImage *src_img1, IplImage *src_img2, IplImage *dst_img){<br />
  int i, j, dx, dy, rows, cols;<br />
  int block_size = 10;<br />
  int shift_size = 1;<br />
  CvMat *velx, *vely;<br />
  CvSize block = cvSize(block_size, block_size);<br />
  CvSize shift = cvSize(shift_size, shift_size);<br />
  CvSize max_range = cvSize(50, 50);<br />
<br />
  rows = int(ceil (double (src_img1-&gt;height) / block_size));<br />
  cols = int(ceil (double (src_img1-&gt;width) / block_size));<br />
  velx = cvCreateMat(rows, cols, CV_32FC1);<br />
  vely = cvCreateMat(rows, cols, CV_32FC1);<br />
  cvSetZero(velx);<br />
  cvSetZero(vely);<br />
<br />
  cvCalcOpticalFlowBM(src_img1, src_img2, block, shift, max_range, 0, velx, vely);<br />
<br />
  for (i = 0; i &lt; velx-&gt;width; i++) {<br />
    for (j = 0; j &lt; vely-&gt;height; j++) {<br />
      dx = (int)cvGetReal2D(velx, j, i);<br />
      dy = (int)cvGetReal2D(vely, j, i);<br />
      cvLine(dst_img, cvPoint(i * block_size, j * block_size),<br />
              cvPoint(i * block_size + dx, j * block_size + dy), CV_RGB(255, 0, 0), 1, CV_AA, 0);<br />
    }<br />
  }<br />
}<br />
</pre>
<br />
<br />
<a href="http://shokai.org/projects/opencv-study-mac/index.cgi/file/f5ca4d2805f2/opticalflow/Makefile">Makefile</a><br />
<pre class="prettyprint">
SRC = image.cpp<br />
DST = image<br />
<br />
prefix=/opt/local<br />
INCPATH=$(prefix)/include<br />
LIBPATH=$(prefix)/lib <br />
<br />
OPT= -lcv -lcvaux -lcxcore -lhighgui <br />
<br />
CC=g++ -O<br />
<br />
CFLAGS= -I$(INCPATH)/opencv<br />
LDFLAGS=-L. -L$(LIBPATH) <br />
<br />
<br />
all:<br />
	$(CC) $(SRC)  -o $(DST) $(CFLAGS)  $(LDFLAGS) $(OPT)<br />
</pre>
</p>
]]></content:encoded>
			<wfw:commentRss>http://shokai.org/blog/archives/4820/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>輪郭で表示</title>
		<link>http://shokai.org/blog/archives/4772</link>
		<comments>http://shokai.org/blog/archives/4772#comments</comments>
		<pubDate>Thu, 27 Aug 2009 10:08:05 +0000</pubDate>
		<dc:creator>shokai</dc:creator>
				<category><![CDATA[未分類]]></category>
		<category><![CDATA[boost]]></category>
		<category><![CDATA[cpp]]></category>
		<category><![CDATA[OpenCV]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[Teleshadow]]></category>

		<guid isPermaLink="false">http://shokai.org/blog/?p=4772</guid>
		<description><![CDATA[アメリカ出張中に、こういう輪郭だけの表示もありかなと思って作ってみたけどまだ実機で試してないからわからないんだった そのうちやる。 ■ダウンロ〜ド Mac OSX 64bit用バイナリ OpenCV勉強リポジトリ ■参考 [...]]]></description>
			<content:encoded><![CDATA[<p>
アメリカ出張中に、こういう輪郭だけの表示もありかなと思って作ってみたけどまだ実機で試してないからわからないんだった<br />
そのうちやる。<br />
<br />
<a href="http://www.flickr.com/photos/shokai/3799087220/" title="輪郭表示 by shokai, on Flickr"><img src="http://farm3.static.flickr.com/2551/3799087220_09c42a5757_o.png" width="960" height="600" alt="輪郭表示" /></a><br />
<br />
<br />
■ダウンロ〜ド<br />
<ul>
<li><a href="http://shokai.org/projects/opencv-study-mac/index.cgi/raw-file/a169841f7d1c/bgsubavg-contour/image">Mac OSX 64bit用バイナリ</a></li>
<li><a href="http://shokai.org/projects/opencv-study-mac/">OpenCV勉強リポジトリ</a></li>
</ul>
<br />
<br />
■参考<br />
この2つを合体させた。<br />
<ul>
<li><a href="http://d.hatena.ne.jp/shokai/20090204/1233775786">赤色領域を検出(4) 輪郭を囲む &#8211; 橋本詳解</a></li>
<li><a href="http://d.hatena.ne.jp/shokai/20090212/1234440251">cvRunningAvgを使って背景統計を取り背景差分する &#8211; 橋本詳解</a></li>
</ul>
<br />
<br />
■boostでprintf風の文字列フォーマット<br />
boost::formatを使う。<br />
<pre class="prettyprint">
#include&nbsp;&lt;boost/format.hpp&gt;<br />
using&nbsp;namespace&nbsp;std;<br />
using&nbsp;namespace&nbsp;boost;<br />
const&nbsp;int&nbsp;INIT_TIME&nbsp;=&nbsp;50;<br />
cout&nbsp;&lt;&lt;&nbsp;str(format("輝度平均&nbsp;%d/%d")&nbsp;%&nbsp;i&nbsp;%&nbsp;INIT_TIME)&nbsp;&lt;&lt;&nbsp;endl;<br />
</pre>
<br />
<br />
■ソースコード<br />
<a href="http://shokai.org/projects/opencv-study-mac/index.cgi/file/a169841f7d1c/bgsubavg-contour/image.cpp">bgsubavg-contour/image.cpp</a><br />
<pre class="prettyprint">
#include&nbsp;&lt;cv.h&gt;<br />
#include&nbsp;&lt;highgui.h&gt;<br />
#include&nbsp;&lt;ctype.h&gt;<br />
#include&nbsp;&lt;stdio.h&gt;<br />
#include&nbsp;&lt;iostream&gt;<br />
#include&nbsp;&lt;boost/format.hpp&gt;<br />
<br />
using&nbsp;namespace&nbsp;std;<br />
using&nbsp;namespace&nbsp;boost;<br />
<br />
int&nbsp;main(int&nbsp;argc,&nbsp;char&nbsp;**argv)<br />
{<br />
&nbsp;&nbsp;const&nbsp;int&nbsp;INIT_TIME&nbsp;=&nbsp;50;<br />
&nbsp;&nbsp;const&nbsp;double&nbsp;BG_RATIO&nbsp;=&nbsp;0.02;&nbsp;//&nbsp;背景領域更新レート<br />
&nbsp;&nbsp;const&nbsp;double&nbsp;OBJ_RATIO&nbsp;=&nbsp;0.005;&nbsp;//&nbsp;物体領域更新レート<br />
&nbsp;&nbsp;const&nbsp;double&nbsp;Zeta&nbsp;=&nbsp;10.0;<br />
&nbsp;&nbsp;IplImage&nbsp;*img&nbsp;=&nbsp;NULL;<br />
<br />
&nbsp;&nbsp;CvMemStorage*&nbsp;storage_contour&nbsp;=&nbsp;cvCreateMemStorage(0);<br />
&nbsp;&nbsp;CvSeq*&nbsp;find_contour&nbsp;=&nbsp;NULL;<br />
<br />
&nbsp;&nbsp;CvCapture&nbsp;*capture&nbsp;=&nbsp;NULL;<br />
&nbsp;&nbsp;capture&nbsp;=&nbsp;cvCreateCameraCapture(0);<br />
&nbsp;&nbsp;//capture&nbsp;=&nbsp;cvCaptureFromAVI("test.avi");<br />
&nbsp;&nbsp;if(capture&nbsp;==&nbsp;NULL){<br />
&nbsp;&nbsp;&nbsp;&nbsp;cerr&nbsp;&lt;&lt;&nbsp;"capture&nbsp;device&nbsp;not&nbsp;found!!"&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;-1;<br />
&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;img&nbsp;=&nbsp;cvQueryFrame(capture);<br />
&nbsp;&nbsp;CvSize&nbsp;size&nbsp;=&nbsp;cvSize(img-&gt;width,&nbsp;img-&gt;height);<br />
<br />
&nbsp;&nbsp;IplImage&nbsp;*imgAverage&nbsp;=&nbsp;cvCreateImage(size,&nbsp;IPL_DEPTH_32F,&nbsp;3);<br />
&nbsp;&nbsp;IplImage&nbsp;*imgSgm&nbsp;=&nbsp;cvCreateImage(size,&nbsp;IPL_DEPTH_32F,&nbsp;3);<br />
&nbsp;&nbsp;IplImage&nbsp;*imgTmp&nbsp;=&nbsp;cvCreateImage(size,&nbsp;IPL_DEPTH_32F,&nbsp;3);<br />
&nbsp;&nbsp;IplImage&nbsp;*img_lower&nbsp;=&nbsp;cvCreateImage(size,&nbsp;IPL_DEPTH_32F,&nbsp;3);<br />
&nbsp;&nbsp;IplImage&nbsp;*img_upper&nbsp;=&nbsp;cvCreateImage(size,&nbsp;IPL_DEPTH_32F,&nbsp;3);<br />
&nbsp;&nbsp;IplImage&nbsp;*imgSilhouette&nbsp;=&nbsp;cvCreateImage(size,&nbsp;IPL_DEPTH_8U,&nbsp;1);<br />
&nbsp;&nbsp;IplImage&nbsp;*imgSilhouetteInv&nbsp;=&nbsp;cvCreateImage(size,&nbsp;IPL_DEPTH_8U,&nbsp;1);<br />
&nbsp;&nbsp;IplImage&nbsp;*imgResult&nbsp;=&nbsp;cvCreateImage(size,&nbsp;IPL_DEPTH_8U,&nbsp;1);<br />
&nbsp;&nbsp;IplImage&nbsp;*imgContour&nbsp;=&nbsp;cvCreateImage(size,&nbsp;IPL_DEPTH_8U,&nbsp;1);<br />
&nbsp;&nbsp;IplImage&nbsp;*imgSilhouette_p&nbsp;=&nbsp;cvCreateImage(size,&nbsp;IPL_DEPTH_8U,&nbsp;1);<br />
<br />
&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;"背景初期化中..."&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;cvSetZero(imgAverage);<br />
&nbsp;&nbsp;for(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;INIT_TIME;&nbsp;i++){<br />
&nbsp;&nbsp;&nbsp;&nbsp;img&nbsp;=&nbsp;cvQueryFrame(capture);<br />
&nbsp;&nbsp;&nbsp;&nbsp;cvAcc(img,&nbsp;imgAverage);<br />
&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;str(format("輝度平均&nbsp;%d/%d")&nbsp;%&nbsp;i&nbsp;%&nbsp;INIT_TIME)&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;cvConvertScale(imgAverage,&nbsp;imgAverage,&nbsp;1.0&nbsp;/&nbsp;INIT_TIME);<br />
&nbsp;&nbsp;cvSetZero(imgSgm);<br />
&nbsp;&nbsp;for(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;INIT_TIME;&nbsp;i++){<br />
&nbsp;&nbsp;&nbsp;&nbsp;img&nbsp;=&nbsp;cvQueryFrame(capture);<br />
&nbsp;&nbsp;&nbsp;&nbsp;cvConvert(img,&nbsp;imgTmp);<br />
&nbsp;&nbsp;&nbsp;&nbsp;cvSub(imgTmp,&nbsp;imgAverage,&nbsp;imgTmp);<br />
&nbsp;&nbsp;&nbsp;&nbsp;cvPow(imgTmp,&nbsp;imgTmp,&nbsp;2.0);<br />
&nbsp;&nbsp;&nbsp;&nbsp;cvConvertScale(imgTmp,&nbsp;imgTmp,&nbsp;2.0);<br />
&nbsp;&nbsp;&nbsp;&nbsp;cvPow(imgTmp,&nbsp;imgTmp,&nbsp;0.5);<br />
&nbsp;&nbsp;&nbsp;&nbsp;cvAcc(imgTmp,&nbsp;imgSgm);<br />
&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;str(format("輝度振幅&nbsp;%d/%d")&nbsp;%&nbsp;i&nbsp;%&nbsp;INIT_TIME)&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;cvConvertScale(imgSgm,&nbsp;imgSgm,&nbsp;1.0&nbsp;/&nbsp;INIT_TIME);<br />
&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;"背景初期化完了"&nbsp;&lt;&lt;&nbsp;endl;<br />
<br />
&nbsp;&nbsp;char&nbsp;winNameCapture[]&nbsp;=&nbsp;"Capture";<br />
&nbsp;&nbsp;char&nbsp;winNameSilhouette[]&nbsp;=&nbsp;"Silhouette";<br />
&nbsp;&nbsp;char&nbsp;winNameContour[]&nbsp;=&nbsp;"Contour";<br />
&nbsp;&nbsp;cvNamedWindow(winNameCapture,&nbsp;CV_WINDOW_AUTOSIZE);<br />
&nbsp;&nbsp;cvNamedWindow(winNameSilhouette,&nbsp;CV_WINDOW_AUTOSIZE);<br />
&nbsp;&nbsp;cvNamedWindow(winNameContour,&nbsp;CV_WINDOW_AUTOSIZE);<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;bool&nbsp;isStop&nbsp;=&nbsp;false;<br />
&nbsp;&nbsp;while(1){<br />
&nbsp;&nbsp;&nbsp;&nbsp;if(!isStop){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;img&nbsp;=&nbsp;cvQueryFrame(capture);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(img&nbsp;==&nbsp;NULL)&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvConvert(img,&nbsp;imgTmp);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;輝度範囲<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvSub(imgAverage,&nbsp;imgSgm,&nbsp;img_lower);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvSubS(img_lower,&nbsp;cvScalarAll(Zeta),&nbsp;img_lower);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvAdd(imgAverage,&nbsp;imgSgm,&nbsp;img_upper);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvAddS(img_upper,&nbsp;cvScalarAll(Zeta),&nbsp;img_upper);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvInRange(imgTmp,&nbsp;img_lower,&nbsp;img_upper,&nbsp;imgSilhouette);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;輝度振幅<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvSub(imgTmp,&nbsp;imgAverage,&nbsp;imgTmp);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvPow(imgTmp,&nbsp;imgTmp,&nbsp;2.0);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvConvertScale(imgTmp,&nbsp;imgTmp,&nbsp;2.0);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvPow(imgTmp,&nbsp;imgTmp,&nbsp;0.5);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;背景領域を更新<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvRunningAvg(img,&nbsp;imgAverage,&nbsp;BG_RATIO,&nbsp;imgSilhouette);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvRunningAvg(imgTmp,&nbsp;imgSgm,&nbsp;BG_RATIO,&nbsp;imgSilhouette);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;物体領域を更新<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvNot(imgSilhouette,&nbsp;imgSilhouetteInv);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvRunningAvg(imgTmp,&nbsp;imgSgm,&nbsp;OBJ_RATIO,&nbsp;imgSilhouetteInv);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvErode(imgSilhouette,&nbsp;imgSilhouette,&nbsp;NULL,&nbsp;2);&nbsp;//&nbsp;収縮<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvDilate(imgSilhouette,&nbsp;imgSilhouette,&nbsp;NULL,&nbsp;4);&nbsp;//&nbsp;膨張<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvErode(imgSilhouette,&nbsp;imgSilhouette,&nbsp;NULL,&nbsp;2);&nbsp;//&nbsp;収縮<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvAnd(imgSilhouette,&nbsp;imgSilhouette_p,&nbsp;imgResult);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;輪郭抽出、青線で囲む<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;contour_num&nbsp;=&nbsp;cvFindContours(cvCloneImage(imgResult),&nbsp;storage_contour,&nbsp;&amp;find_contour,<br />
				&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sizeof(CvContour),&nbsp;CV_RETR_LIST,&nbsp;CV_CHAIN_APPROX_NONE,&nbsp;<br />
				&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvPoint(0,0));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CvScalar&nbsp;white&nbsp;=&nbsp;CV_RGB(255,255,255);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvSetZero(imgContour);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvDrawContours(imgContour,&nbsp;find_contour,&nbsp;white,&nbsp;white,&nbsp;2,&nbsp;2,&nbsp;8,&nbsp;cvPoint(0,0));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvNot(imgContour,&nbsp;imgContour);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvShowImage(winNameCapture,&nbsp;img);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvShowImage(winNameSilhouette,&nbsp;imgResult);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvShowImage(winNameContour,&nbsp;imgContour);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cvCopy(imgSilhouette,&nbsp;imgSilhouette_p);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;waitKey&nbsp;=&nbsp;cvWaitKey(33);<br />
&nbsp;&nbsp;&nbsp;&nbsp;if(waitKey&nbsp;==&nbsp;'q')&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;&nbsp;if(waitKey&nbsp;==&nbsp;'&nbsp;'){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;isStop&nbsp;=&nbsp;!isStop;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(isStop)&nbsp;cout&nbsp;&lt;&lt;&nbsp;"stop"&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else&nbsp;cout&nbsp;&lt;&lt;&nbsp;"start"&nbsp;&lt;&lt;&nbsp;endl;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;cvReleaseCapture(&amp;capture);<br />
&nbsp;&nbsp;cvDestroyWindow(winNameCapture);<br />
&nbsp;&nbsp;cvDestroyWindow(winNameSilhouette);<br />
&nbsp;&nbsp;cvDestroyWindow(winNameContour);<br />
&nbsp;&nbsp;return&nbsp;0;<br />
}<br />
</pre>
<br />
<br />
<a href="http://shokai.org/projects/opencv-study-mac/index.cgi/file/a169841f7d1c/bgsubavg-contour/Makefile">Makefile</a><br />
<pre class="prettyprint">
SRC&nbsp;=&nbsp;image.cpp<br />
DST&nbsp;=&nbsp;image<br />
<br />
prefix=/opt/local<br />
INCPATH=$(prefix)/include<br />
LIBPATH=$(prefix)/lib&nbsp;<br />
<br />
OPT=&nbsp;-lcv&nbsp;-lcvaux&nbsp;-lcxcore&nbsp;-lhighgui&nbsp;<br />
<br />
CC=g++&nbsp;-O<br />
<br />
CFLAGS=&nbsp;-I$(INCPATH)/opencv<br />
LDFLAGS=-L.&nbsp;-L$(LIBPATH)&nbsp;<br />
<br />
<br />
all:<br />
	$(CC)&nbsp;$(SRC)&nbsp;&nbsp;-o&nbsp;$(DST)&nbsp;$(CFLAGS)&nbsp;&nbsp;$(LDFLAGS)&nbsp;$(OPT)<br />
</pre>
</p>
]]></content:encoded>
			<wfw:commentRss>http://shokai.org/blog/archives/4772/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

