<?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>More Than Technical &#187; gui</title>
	<atom:link href="http://www.morethantechnical.com/category/gui/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.morethantechnical.com</link>
	<description>On software, code, the internet and more.</description>
	<lastBuildDate>Mon, 23 Aug 2010 10:51:39 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=abc</generator>
<atom:link rel="hub" href="http://pubsubhubbub.appspot.com"/><atom:link rel="hub" href="http://superfeedr.com/hubbub"/>		<item>
		<title>Combining OpenCV 2.x and Microsoft WPF [w/ code]</title>
		<link>http://www.morethantechnical.com/2010/07/21/combining-opencv-2-x-and-microsoft-wpf-w-code/</link>
		<comments>http://www.morethantechnical.com/2010/07/21/combining-opencv-2-x-and-microsoft-wpf-w-code/#comments</comments>
		<pubDate>Wed, 21 Jul 2010 21:30:59 +0000</pubDate>
		<dc:creator>Roy</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[graphics]]></category>
		<category><![CDATA[gui]]></category>
		<category><![CDATA[opencv]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[tips]]></category>
		<category><![CDATA[wpf]]></category>

		<guid isPermaLink="false">http://www.morethantechnical.com/?p=687</guid>
		<description><![CDATA[Hi, Just a quicky about OpenCV and Windows Presentation Framework interoperability. It&#8217;s really easy with a simple Managed C++ wrapper. What I&#8217;ll show is how to transfer an OpenCV cv::Mat into WPF&#8217;s BitmapSource. Let&#8217;s see how it&#8217;s done. First some framework. I&#8217;ll define a managed C++ (ref) class that holds everything WPF needs to make [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.morethantechnical.com/wp-content/uploads/2010/07/opencv_mswpf.png" rel="lightbox[687]"><img src="http://www.morethantechnical.com/wp-content/uploads/2010/07/opencv_mswpf-300x162.png" alt="" title="opencv_mswpf" width="300" height="162" class="alignleft size-medium wp-image-692" /></a>Hi,<br />
Just a quicky about OpenCV and Windows Presentation Framework interoperability. It&#8217;s really easy with a simple Managed C++ wrapper. What I&#8217;ll show is how to transfer an OpenCV cv::Mat into WPF&#8217;s BitmapSource. Let&#8217;s see how it&#8217;s done.<br />
<span id="more-687"></span><br />
First some framework. I&#8217;ll define a managed C++ (ref) class that holds everything WPF needs to make a BitmapSource:</p>
<pre class="brush: plain;">
public ref class OpenCVImageWrapper {
	public:
		int width;
		int height;
		cli::array&lt;byte&gt;^ buf;
		int buf_size;
		int row_stride;
		int channels;
	};
</pre>
<p>That&#8217;s it, now let&#8217;s build a BitmapSource out of an instance of this class in C#:</p>
<pre class="brush: plain;">
OpenCVImageWrapper im = getFromOpenCV();
BitmapSource bs = BitmapSource.Create(
                im.width, im.height, 96, 96,
                System.Windows.Media.PixelFormats.Bgr24, //need to look out here: this is for BGR 24-bits images, you might need Gray8, Gray32Float or one of others...
                null, im.buf, im.row_stride);
imageOnScreen.Source = bs;
</pre>
<p>(imageOnScreen is an Image object I dragged and dropped into a window in the visual GUI builder)</p>
<p>Now we need a bit more managed C++ wizardry to fill up the wrapper, here it is:</p>
<pre class="brush: plain;">
OpenCVImageWrapper^ getFromOpenCV() {
Mat im = imread(&quot;someimage.jpg&quot;);

OpenCVImageWrapper^ i = gcnew OpenCVImageWrapper();

i-&gt;buf = gcnew cli::array&lt;byte&gt;(im.rows * im.step);
System::Runtime::InteropServices::Marshal::Copy(System::IntPtr::IntPtr(im.data),i-&gt;buf,0,im.rows * im.step);

i-&gt;width = im.cols;
i-&gt;height = im.rows;
i-&gt;row_stride = im.step;
i-&gt;channels = im.channels();
i-&gt;buf_size = im.rows * im.step;

return i;
}
</pre>
<p>We&#8217;re pretty much done. Basic stuff!</p>
<p>It&#8217;s not memory optimized as I&#8217;m copying the buffer, but it gets the job done.</p>
<p>Thanks<br />
Roy.</p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fwww.morethantechnical.com%2F2010%2F07%2F21%2Fcombining-opencv-2-x-and-microsoft-wpf-w-code%2F&amp;linkname=Combining%20OpenCV%202.x%20and%20Microsoft%20WPF%20%5Bw%2F%20code%5D"><img src="http://www.morethantechnical.com/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share/Bookmark"/></a> </p>]]></content:encoded>
			<wfw:commentRss>http://www.morethantechnical.com/2010/07/21/combining-opencv-2-x-and-microsoft-wpf-w-code/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Image Recoloring using Gaussian Mixture Model and Expectation Maximization [OpenCV, w/Code]</title>
		<link>http://www.morethantechnical.com/2010/06/24/image-recoloring-using-gaussian-mixture-model-and-expectation-maximization-opencv-wcode/</link>
		<comments>http://www.morethantechnical.com/2010/06/24/image-recoloring-using-gaussian-mixture-model-and-expectation-maximization-opencv-wcode/#comments</comments>
		<pubDate>Thu, 24 Jun 2010 15:34:59 +0000</pubDate>
		<dc:creator>Roy</dc:creator>
				<category><![CDATA[Recommended]]></category>
		<category><![CDATA[Website]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[graphics]]></category>
		<category><![CDATA[gui]]></category>
		<category><![CDATA[opencv]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[school]]></category>
		<category><![CDATA[vision]]></category>
		<category><![CDATA[expectation maximization]]></category>
		<category><![CDATA[gaussian]]></category>
		<category><![CDATA[mixture model]]></category>

		<guid isPermaLink="false">http://www.morethantechnical.com/?p=673</guid>
		<description><![CDATA[Hi, I&#8217;ll present a quick and simple implementation of image recoloring, in fact more like color transfer between images, using OpenCV in C++ environment. The basis of the algorithm is learning the source color distribution with a GMM using EM, and then applying changes to the target color distribution. It&#8217;s fairly easy to implement with [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.morethantechnical.com/wp-content/uploads/2010/06/eggplant_orange.png" rel="lightbox[673]"><img src="http://www.morethantechnical.com/wp-content/uploads/2010/06/eggplant_orange.png" alt="" title="eggplant_orange" width="654" height="187" class="alignleft size-full wp-image-684" /></a>Hi,<br />
I&#8217;ll present a quick and simple implementation of image recoloring, in fact more like color transfer between images, using OpenCV in C++ environment. The basis of the algorithm is learning the source color distribution with a GMM using EM, and then applying changes to the target color distribution. It&#8217;s fairly easy to implement with OpenCV, as all the &#8220;tools&#8221; are built in.</p>
<p>I was inspired by <a href="http://www.cs.tau.ac.il/~liors/research/papers/image_appearance_exploration.pdf">Lior Shapira&#8217;s work</a> that was presented in Eurographics 09 about image appearance manipulation, and a work  about recoloring for the colorblind by <a href="http://www.sciweavers.org/files/docs/2358/icassp_cvd_poster_pdf_4a383d1fb0.pdf">Huang et al</a> presented at ICASSP 09. Both works deal with color manipulation using Gaussian Mixture Models.</p>
<p>Let&#8217;s see how it&#8217;s done!<br />
<span id="more-673"></span></p>
<h2>A little theory</h2>
<p>I won&#8217;t bore you with the math, but just to get a hang of the idea, what we would like to do is learn how the colors in the source and target images are distributed. Naturally we can assume, like many other things in nature, the colors in a picture have a <a href="http://en.wikipedia.org/wiki/Normal_distribution">normal (Gaussian) distribution</a>, but we can go further by saying the distribution might have <strong>a few Gaussians</strong> describing it. This is called a <a href="http://en.wikipedia.org/wiki/Mixture_distribution">mixture distribution</a>, and it&#8217;s a very handy statistical tool. Mixtures can be estimated using a powerful and ubiquitous tool called <a href="http://en.wikipedia.org/wiki/Expectation_maximization">Expectation Maximization</a>, which I have previously covered. EM essentially tries to recover the mean (mu) and variance (sigma) of the Gaussians in the mixture, by iteratively checking the current hypothesis against the data until finally converging at an extremum.</p>
<h2>Learning the color model</h2>
<p>For the learning process we must set up the sample data. So we create a binary mask saying where in the image the model can find the colors to learn.<br />
&#8211;images&#8211;<br />
Then we scan the mask and for each foreground pixel we add it&#8217;s value (here, RGB, but basically can be anything) to the sample data. Finally we train the CvEM object, which contains the GMM parameters.</p>
<pre class="brush: plain;">
void TrainGMM(CvEM&amp; source_model, Mat&amp; source, Mat&amp; source_mask) {
		int src_samples_size = countNonZero(source_mask);
		Mat source_samples(src_samples_size,3,CV_32FC1);

		Mat source_32f;
		source_32f = source;

		int sample_count = 0;
		for(int y=0;y&lt;source.rows;y++) {
			Vec3f* row = source_32f.ptr&lt;Vec3f&gt;(y);  //pointer to pixel data in the row
			uchar* mask_row = source_mask.ptr&lt;uchar&gt;(y); //pointer to binary mask
			for(int x=0;x&lt;source.cols;x++) {
				if(mask_row[x] &gt; 0) {
					source_samples.at&lt;Vec3f&gt;(sample_count++,0) = row[x];
				}
			}
		}

		source_model.clear();
		CvEMParams ps(3/* = number of gaussians*/);
		source_model.train(source_samples,Mat(),ps,NULL);
}
</pre>
<p>What we have are three 3-dimensional (R,G,B) Gaussians, describing the colors in the selected area.</p>
<h2>Matching Gaussians</h2>
<p>Now we have a couple of GMMs &#8211; one for the target and one for the source. The idea is to take a pixel in the target, see how the 3 target Gaussians describe it, and shift it to use the 3 source Gaussians. This will, hopefully, cause its color to change from target to source. But we need to know which Gaussian in the target corresponds to which Gaussian in the source. I made a quick selection algorithm that greedily chooses a Gaussian for each Gaussian. I permutate the order of selection for the greedy algorithm, and pick the best permutation to get closer to the optimal selection.</p>
<pre class="brush: plain;">
vector&lt;int&gt; Recoloring::MatchGaussians(CvEM&amp; source_model, CvEM&amp; target_model) {
		int num_g = source_model.get_nclusters();
		Mat sMu(source_model.get_means());
		Mat tMu(target_model.get_means());
		const CvMat** target_covs = target_model.get_covs();
		const CvMat** source_covs = source_model.get_covs();

		double best_dist = std::numeric_limits&lt;double&gt;::max();
		vector&lt;int&gt; best_res(num_g);
		vector&lt;int&gt; prmt(num_g); 

		for(int itr = 0; itr &lt; 10; itr++) {
			for(int i=0;i&lt;num_g;i++) prmt[i] = i;	//make a permutation
			randShuffle(Mat(prmt));

			//Greedy selection
			vector&lt;int&gt; res(num_g);
			vector&lt;bool&gt; taken(num_g);
			for(int sg = 0; sg &lt; num_g; sg++) {
				double min_dist = std::numeric_limits&lt;double&gt;::max();
				int minv = -1;
				for(int tg = 0; tg &lt; num_g; tg++) {
					if(taken[tg]) continue;

					//TODO: can save on re-calculation of pairs - calculate affinity matrix ahead
					//double d = norm(sMu(Range(prmt[sg],prmt[sg]+1),Range(0,3)),	tMu(Range(tg,tg+1),Range(0,3)));

					//symmetric kullback-leibler
					Mat diff = Mat(sMu(Range(prmt[sg],prmt[sg]+1),Range(0,3)) - tMu(Range(tg,tg+1),Range(0,3)));
					Mat d = diff * Mat(Mat(source_covs[prmt[sg]]).inv() + Mat(target_covs[tg]).inv()) * diff.t();
					Scalar tr = trace(Mat(
						Mat(Mat(source_covs[prmt[sg]])*Mat(target_covs[tg])) +
						Mat(Mat(target_covs[tg])*Mat(source_covs[prmt[sg]]).inv()) +
						Mat(Mat::eye(3,3,CV_64FC1)*2)
						));
					double kl_dist = ((double*)d.data)[0] + tr[0];
					if(kl_dist&lt;min_dist) {
						min_dist = kl_dist;
						minv = tg;
					}
				}
				res[prmt[sg]] = minv;
				taken[minv] = true;
			}

                       //total distance for the permutation
			double dist = 0;
			for(int i=0;i&lt;num_g;i++) {
				dist += norm(sMu(Range(prmt[i],prmt[i]+1),Range(0,3)),
							tMu(Range(res[prmt[i]],res[prmt[i]]+1),Range(0,3)));
			}
			if(dist &lt; best_dist) {
				best_dist = dist;
				best_res = res;
			}
		}

		return best_res;
	}
</pre>
<p>I used Symmetric Kullback-Leibler for the distance between Gaussians, as suggested by Huang et al.</p>
<h2>Applying the color</h2>
<p>Now all we have to do is use the method Shapira suggested in his work to transform a pixel&#8217;s color, from the Gaussians describing it.<br />
I&#8217;m only putting the essence of the code, the rest is in the file.</p>
<pre class="brush: plain;">
		Mat pr; Mat samp(1,3,CV_32FC1);
		for(int y=0;y&lt;target.rows;y++) {
			Vec3f* row = target_32f.ptr&lt;Vec3f&gt;(y);
			uchar* mask_row = target_mask.ptr&lt;uchar&gt;(y);
			for(int x=0;x&lt;target.cols;x++) {
				if(mask_row[x] &gt; 0) {
                                        //take pixel data
					memcpy(samp.data,&amp;(row[x][0]),3*sizeof(float)); 

                                        //Use the GMM to predict how close this pixel is to each gaussian
					float res = target_model.predict(samp,&amp;pr);

					Mat samp_64f; samp.convertTo(samp_64f,CV_64F);

                                        //Move the pixel to the new Gaussians
					//From Shapira09: Xnew = Sum_i { pr(i) * Sigma_source_i * (Sigma_target_i)^-1 * (x - mu_target) + mu_source }
					Mat Xnew(1,3,CV_64FC1,Scalar(0));
					for(int i=0;i&lt;num_g;i++) {
						if(((float*)pr.data)[i] &lt;= 0) continue;

                                               //For each Gaussian, subtract the original mean and add the target mean,
                                               //use probabilities to get a weighted average.
						Xnew += Mat((
							//Mat(source_covs[match[i]]) *
							//Mat(target_covs[i]).inv() *
							Mat(samp_64f - tMu_64f(Range(i,i+1),Range(0,3))).t() +
							sMu_64f(Range(match[i],match[i]+1),Range(0,3)).t()
							) * (double)(((float*)pr.data)[i])).t();
					}

                                        //Put pixel back into place
					Mat _tmp; Xnew.convertTo(_tmp,CV_32F);
					memcpy(&amp;(row[x][0]),_tmp.data,sizeof(float)*3);
				}
			}
		}
</pre>
<p>You might notice I skip the part of multiplying by the covariances matrices, as Shapira did. I found it produces better results, but it&#8217;s probably caused by a bug.</p>
<h2>Results</h2>
<p><a href="http://www.morethantechnical.com/wp-content/uploads/2010/06/recoloring_result1.png" rel="lightbox[673]"><img src="http://www.morethantechnical.com/wp-content/uploads/2010/06/recoloring_result1-300x83.png" alt="" title="recoloring_result1" width="300" height="83" class="aligncenter size-medium wp-image-675" /></a><br />
<a href="http://www.morethantechnical.com/wp-content/uploads/2010/06/recoloring_result.png" rel="lightbox[673]"><img src="http://www.morethantechnical.com/wp-content/uploads/2010/06/recoloring_result-300x79.png" alt="" title="recoloring_result" width="300" height="79" class="aligncenter size-medium wp-image-674" /></a></p>
<h2>Code and stuff</h2>
<p>Source code is available in SVN repo:</p>
<pre class="brush: plain;">
svn checkout http://morethantechnical.googlecode.com/svn/trunk/GMM_Recoloring recoloring
</pre>
<p>Images from Flickr (Creative Commons):</p>
<ul>
<li>http://www.flickr.com/photos/wwworks/2956622857/sizes/s/</li>
<li>http://www.flickr.com/photos/violentz/3199292482/sizes/s/</li>
<li>http://www.flickr.com/photos/davidw/164670455/sizes/m/</li>
<li>http://www.flickr.com/photos/djania/252225693/sizes/m/</li>
</ul>
<p>Now go recolor the world!</p>
<p>Thanks for tuning in..<br />
Roy.</p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fwww.morethantechnical.com%2F2010%2F06%2F24%2Fimage-recoloring-using-gaussian-mixture-model-and-expectation-maximization-opencv-wcode%2F&amp;linkname=Image%20Recoloring%20using%20Gaussian%20Mixture%20Model%20and%20Expectation%20Maximization%20%5BOpenCV%2C%20w%2FCode%5D"><img src="http://www.morethantechnical.com/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share/Bookmark"/></a> </p>]]></content:encoded>
			<wfw:commentRss>http://www.morethantechnical.com/2010/06/24/image-recoloring-using-gaussian-mixture-model-and-expectation-maximization-opencv-wcode/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Awesome pictures fusing with a GIMP plugin [w/ code]</title>
		<link>http://www.morethantechnical.com/2009/09/21/awesome-pictures-fusing-with-a-gimp-plugin-w-code/</link>
		<comments>http://www.morethantechnical.com/2009/09/21/awesome-pictures-fusing-with-a-gimp-plugin-w-code/#comments</comments>
		<pubDate>Mon, 21 Sep 2009 12:58:58 +0000</pubDate>
		<dc:creator>Roy</dc:creator>
				<category><![CDATA[graphics]]></category>
		<category><![CDATA[gui]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[video]]></category>
		<category><![CDATA[gimp]]></category>

		<guid isPermaLink="false">http://www.morethantechnical.com/?p=381</guid>
		<description><![CDATA[Switching, merging or swapping, call it what you like &#8211; it&#8217;s a pain to pull off. You need to spend a lot of time tuning the colors, blending the edges and smudging to get a decent result. So I wrote a plugin for the wonderful GIMP program that helps this process. The merge is done [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.morethantechnical.com/wp-content/uploads/2009/09/desert_bear_arrow.png" rel="lightbox[381]"><img src="http://www.morethantechnical.com/wp-content/uploads/2009/09/desert_bear_arrow.png" alt="desert_bear_arrow" title="desert_bear_arrow" width="320" height="240" class="alignleft size-full wp-image-462" /></a>Switching, merging or swapping, call it what you like &#8211; it&#8217;s a pain to pull off. You need to spend a lot of time tuning the colors, blending the edges and smudging to get a decent result. So I wrote a plugin for the wonderful GIMP program that helps this process. The merge is done using a blending algorithm that blends in the colous from the original image into the pasted image.</p>
<p>I&#8217;ll write a little bit about coding GIMP plugins, which is very simple, and some about the algorithm and its sources.</p>
<p>Let&#8217;s see how it&#8217;s done<br />
<span id="more-381"></span></p>
<h2>Writing a GIMP plugin</h2>
<p>As I mentioned, writing a plugin for the GIMP is not very hard. I used the excellent (though outdated) <a href="http://developer.gimp.org/writing-a-plug-in/1/index.html">guide for writing a plugin</a> from GIMP.org.<br />
There are 2 kinds of plugins: scripts, and &#8220;native&#8221; plugins. Script plugins are written in script-fu, the scripting language for GIMP, they are not compiled. On the opposite side, the &#8220;native&#8221; plugins are written either in C and compiled to executable, or in Python or Perl. I wrote my plugin in C based roughly on the <a href="http://developer.gimp.org/plug-in-template.html">plugin template</a>, and compiled both on Linux and windows.</p>
<h3>From the ground up</h3>
<p>The first step I did was getting my hands on the pixel data of the selected layer. This is covered pretty nicely in the <a href="http://developer.gimp.org/writing-a-plug-in/2/index.html">tutorial</a>.<br />
Once you get a GimpDrawble you just use gimp_pixel_rgn_init, and then gimp_pixel_rgn_get_row to get a whole row of pixel data (interleaved RGB/RGBA).</p>
<p>To access elements in the row of bytes you need to know how many channels are in the interleave.</p>
<pre class="brush: plain;">
gint         channels, x1, y1, x2, y2, x_off, y_off, m, n;
GimpPixelRgn rgn_in;
guchar       *row;

gimp_drawable_mask_bounds (drawable-&gt;drawable_id,
	                                   &amp;x1, &amp;y1,
	                                   &amp;x2, &amp;y2);
gimp_drawable_offsets(drawable-&gt;drawable_id,&amp;x_off,&amp;y_off);
m = y2 - y1; //height of relevant region
n = x2 - x1; //width of relevant region

channels = gimp_drawable_bpp (drawable-&gt;drawable_id);
gimp_pixel_rgn_init (&amp;rgn_in,
			 drawable,
			 x1, y1,
			 n, m,
			 FALSE, FALSE);
row = g_new (guchar, channels * n);
for (int y = 0; y &lt; m; y++) {
gimp_pixel_rgn_get_row (&amp;rgn_in, 		row, 		0, y, n);

//manipulate pixels...
}
</pre>
<p>Working with layers masks is also simple. Masks are GimpDrawables as well.</p>
<pre class="brush: plain;">
gint32 mask_id = gimp_layer_get_mask(drawable-&gt;drawable_id);
GimpDrawable* mask_drawable = gimp_drawable_get(mask_id);
</pre>
<h3>Progress reporting</h3>
<p>During the run of a lengthy operation it&#8217;s recommended to notify the user of the progress. The easiest way to do it in a GIMP plugin is use the built-in progress bar. Very easy to use:</p>
<pre class="brush: plain;">
gimp_progress_init (&quot;Clone...&quot;);
....
gimp_progress_set_text_printf(&quot;Create matrix %dx%d&quot;,mn,mn);
gimp_progress_update(0.33);
....
gimp_progress_update(1.0);
</pre>
<h3>User inputs</h3>
<p>User input for plugins can be easily done using GTK dialogs. The <a href="http://developer.gimp.org/writing-a-plug-in/3/index.html">gimp plugin development tutorial</a> is covering this briefly, but its fairly simple for someone who is already fimiliar with GTK. I copied off the tutorial, and changed the spinbutton to a checkbutton for my needs.</p>
<p><a href="http://www.morethantechnical.com/wp-content/uploads/2009/09/dialog.png" rel="lightbox[381]"><img src="http://www.morethantechnical.com/wp-content/uploads/2009/09/dialog.png" alt="dialog" title="dialog" width="299" height="140" class="alignleft size-full wp-image-443" /></a></p>
<p>One tip: in the tutorial code the gimp_dialog_new function will not accept just an int for the 4th parameter (needs GtkDialogFlags), so I passed in GtkDialogFlags::GTK_DIALOG_MODAL.</p>
<p>Now that we got the bases covered, let&#8217;s move on to the algorithm.</p>
<h2>The blending algorithm</h2>
<p>The algorithm is based on a <a href="http://www.irisa.fr/vista/Papers/2003_siggraph_perez.pdf">paper published in 03&#8242; by Microsoft Research</a>. The article presents a way to merge two images by inspecting their gradients, and using this information to &#8220;bleed&#8221; in colors from the background image into the pasted image. More generally, the colors from the outer image (the background) will seep into the pasted image, and they will do it faster if the pasted image is more smooth. This creates a very nice blending effect.</p>
<p>There&#8217;s some mathematical mumbo-jumbo about estimating and discretising the calculation of the gradient, and in the end it boils down to a simple sparse matrix multiplication. I used <a href="http://home.gna.org/getfem/gmm_intro.html">GMM++</a> library to do this in C environment. The library compiles from source (only .h files), so you only need to #include it and set the project paths correctly.</p>
<h2>How to use the plugin</h2>
<p>OK, so all this BS about GIMP, C and math gave me a headache, and we&#8217;re here to have some fun after all. So without further ado, let&#8217;s see how to use this plugin to do some nice tricks. Originally I used this plugin to merge faces together, but it actually can be used to merge anything.</p>
<p>For example, a polar bear in the desert:<br />
<a href="http://www.morethantechnical.com/wp-content/uploads/2009/09/desert_bear.png" rel="lightbox[381]"><img src="http://www.morethantechnical.com/wp-content/uploads/2009/09/desert_bear-300x225.png" alt="desert_bear" title="desert_bear" width="300" height="225" class="alignnone size-medium wp-image-444" /></a></p>
<p>This is how to do it:</p>
<ol>
<li>get a background picture, preferably one with a uniform color part on which you&#8217;ll want to blend in the external object</li>
<p><a href="http://www.morethantechnical.com/wp-content/uploads/2009/09/desert.png" rel="lightbox[381]"><img src="http://www.morethantechnical.com/wp-content/uploads/2009/09/desert.png" alt="desert" title="desert" width="320" height="240" class="alignnone size-full wp-image-445" /></a></p>
<li>get an image of an object, preferably surrounded by a uniform color &#8220;buffer&#8221; so it will blend nicely into the background</li>
<p><a href="http://www.morethantechnical.com/wp-content/uploads/2009/09/bear.jpeg" rel="lightbox[381]"><img src="http://www.morethantechnical.com/wp-content/uploads/2009/09/bear.jpeg" alt="bear" title="bear" width="124" height="93" class="alignnone size-full wp-image-446" /></a></p>
<li>if needed, cut out a bounding rectangle of the object with a good margin on all sides, and place it where you want on the background</li>
<p><a href="http://www.morethantechnical.com/wp-content/uploads/2009/09/desert_bear_no_merge.png" rel="lightbox[381]"><img src="http://www.morethantechnical.com/wp-content/uploads/2009/09/desert_bear_no_merge-300x225.png" alt="desert_bear_no_merge" title="desert_bear_no_merge" width="300" height="225" class="alignnone size-medium wp-image-447" /></a></p>
<li>create a mask, and use lasso to create a tighter mask around the object</li>
<p><a href="http://www.morethantechnical.com/wp-content/uploads/2009/09/desert_bear_cut.png" rel="lightbox[381]"><img src="http://www.morethantechnical.com/wp-content/uploads/2009/09/desert_bear_cut.png" alt="desert_bear_cut" title="desert_bear_cut" width="570" height="268" class="alignnone size-full wp-image-448" /></a></p>
<li>select the object layer, and fire the plugin</li>
<p><a href="http://www.morethantechnical.com/wp-content/uploads/2009/09/desert_bear_filer_menu.png" rel="lightbox[381]"><img src="http://www.morethantechnical.com/wp-content/uploads/2009/09/desert_bear_filer_menu.png" alt="desert_bear_filer_menu" title="desert_bear_filer_menu" width="640" height="480" class="alignnone size-full wp-image-449" /></a></p>
<ul>
<p>Some other stuff I did:<br />
<a href="http://www.morethantechnical.com/wp-content/uploads/2009/09/moon_mouse.png" rel="lightbox[381]"><img src="http://www.morethantechnical.com/wp-content/uploads/2009/09/moon_mouse.png" alt="moon_mouse" title="moon_mouse" width="320" height="240" class="alignnone size-full wp-image-455" /></a><br />
<a href="http://www.morethantechnical.com/wp-content/uploads/2009/09/sea_shark.png" rel="lightbox[381]"><img src="http://www.morethantechnical.com/wp-content/uploads/2009/09/sea_shark.png" alt="sea_shark" title="sea_shark" width="320" height="240" class="alignnone size-full wp-image-456" /></a></p>
<h3>Video time&#8230;</h3>
<p><object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/Wo_WghEqpPA&#038;hl=en&#038;fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/Wo_WghEqpPA&#038;hl=en&#038;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object></p>
<h2>Source of course</h2>
<p>Source is as usual in our <a href="http://code.google.com/p/morethantechnical/source/browse/#svn/trunk/ImageCloningGIMPPlugin">Google Code SVN repo</a>.</p>
<p>Executable (win32) is available <a href="http://morethantechnical.googlecode.com/files/ImageCloningGIMPPlugin.exe">here</a>.</p>
<p>Thank you for tuning in!<br />
Roy.</p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fwww.morethantechnical.com%2F2009%2F09%2F21%2Fawesome-pictures-fusing-with-a-gimp-plugin-w-code%2F&amp;linkname=Awesome%20pictures%20fusing%20with%20a%20GIMP%20plugin%20%5Bw%2F%20code%5D"><img src="http://www.morethantechnical.com/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share/Bookmark"/></a> </p>]]></content:encoded>
			<wfw:commentRss>http://www.morethantechnical.com/2009/09/21/awesome-pictures-fusing-with-a-gimp-plugin-w-code/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Advanced topics in 3D game building [w/ code, video]</title>
		<link>http://www.morethantechnical.com/2009/07/27/advanced-issues-in-3d-game-building-with-jogl-openglswt-w-code-video/</link>
		<comments>http://www.morethantechnical.com/2009/07/27/advanced-issues-in-3d-game-building-with-jogl-openglswt-w-code-video/#comments</comments>
		<pubDate>Mon, 27 Jul 2009 12:17:27 +0000</pubDate>
		<dc:creator>Roy</dc:creator>
				<category><![CDATA[3d]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[graphics]]></category>
		<category><![CDATA[gui]]></category>
		<category><![CDATA[opengl]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[school]]></category>
		<category><![CDATA[video]]></category>
		<category><![CDATA[game]]></category>
		<category><![CDATA[jogl]]></category>
		<category><![CDATA[swt]]></category>

		<guid isPermaLink="false">http://www.morethantechnical.com/?p=325</guid>
		<description><![CDATA[Hi The graphics course I took at TAU really expanded my knowledge of 3D rendering, and specifically using OpenGL to do so. The final task of the course, aside from the exam, was to write a 3D game. We were given 3 choices for types of games: worms-like, xonix-like and lightcycle-like. We chose to write [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.morethantechnical.com/wp-content/uploads/2009/07/snails_3d.png" rel="lightbox[325]"><img src="http://www.morethantechnical.com/wp-content/uploads/2009/07/snails_3d-300x223.png" alt="snails_3d" title="snails_3d" width="300" height="223" class="alignleft size-medium wp-image-364" /></a>Hi</p>
<p>The graphics course I took at TAU really expanded my knowledge of 3D rendering, and specifically using OpenGL to do so. The final task of the course, aside from the exam, was to write a 3D game. We were given 3 choices for types of games: worms-like, xonix-like and lightcycle-like. We chose to write our version of Worms in 3D.</p>
<p>I&#8217;ll try to take you through some of the problems we encountered, the decisions we made, and show as much code as possible. I&#8217;m not, however, gonna take you through the simple (yet grueling) work of actually showing meshes to the screen or moving them around, these subjects are covered extensively online.</p>
<p>The whole game is implemented in Java using JOGL and SWT for 3D rendering. The code is of course available entirely <a href="http://code.google.com/p/taucomputergraphics09/source/browse/#svn/trunk/CG_EX3">online</a>.</p>
<p><span id="more-325"></span></p>
<h2>In the begining there was mesh</h2>
<p>Our game is a turn-based shooter, Worms-like. The players are supposed to walk on the face of a world mesh (a 3d structure), and shoot missiles at each other.<br />
When it comes to positioning and aligning 3D objects on top of other 3D objects, I must say I was overwhelmed by the complexity. Its really not that easy to do, but once you get the idea its easy.<br />
So you have an arbitrary 3D object, and you want another object to &#8220;walk&#8221; over it. Few things you need to know:</p>
<ol>
<li>the normal of the mesh at any given point, so your player can have an &#8220;up&#8221; direction (away from the mesh)</li>
<li>the global orientation of your player object, so when you align it up it will actually face up</li>
</ol>
<p>Now, to align the object you have to make it be oriented upwards from the mesh and point in the forward direction. So before drawing the player object make the model-view matrix face the right direction. For this we <a href="http://www.opengl.org/resources/faq/technical/lookat.cpp">ported some CPP code</a> that does exactly that:</p>
<pre class="brush: plain;">
public static void multLookAt (/*float eyex, float eyey, float eyez,
            float atx, float aty, float atz,
            float upx, float upy, float upz,*/
			Vector3D origin,
			Vector3D forward,
			Vector3D up,
            GL gl)
	{
		float m[] = new float[16];

		// Compute our new look at vector, which will be
		//   the new negative Z axis of our transformed object.
		forward = forward.getNormalized();

		// Cross product of the new look at vector and the current
		//   up vector will produce a vector which is the new
		//   positive X axis of our transformed object.
		Vector3D xaxis = forward.crossProduct(up).getNormalized();
		m[0] = xaxis.x;
		m[1] = xaxis.y;
		m[2] = xaxis.z;

		// Calculate the new up vector, which will be the
		//   positive Y axis of our transformed object. Note
		//   that it will lie in the same plane as the new
		//   look at vector and the old up vector.
		up = xaxis.crossProduct(forward);
		m[4] = up.x;
		m[5] = up.y;
		m[6] = up.z;

		// Account for the fact that the geometry will be defined to
		//   point along the negative Z axis.
		forward = forward.multiply(-1f);
		m[8] = forward.x;
		m[9] = forward.y;
		m[10] = forward.z;

		// Fill out the rest of the 4x4 matrix
		m[3] = 0.f;     // xaxis is m[0..2]
		m[7] = 0.f;     // up is m[4..6]
		m[11] = 0.f;    // -at is m[8..10]
		m[12] = origin.x;
		m[13] = origin.y;
		m[14] = origin.z;
		m[15] = 1.f;

		// Multiply onto current matrix stack.
		gl.glMultMatrixf(FloatBuffer.wrap(m));
	}
</pre>
<h2>Walk the mesh</h2>
<p>Another key point in our game is making the player character walk smoothly on the mesh. The instructions for this exercise were that we have to make the player either walk from vertex to vertex, from edge to edge or the most complicated option &#8211; an arbitrary point on the mesh to another. We chose to make the player walk from edge to edge.<br />
Say that the player object is on an edge. To make it walk to another edge we needed to know what are the possible edges that it could reach. For that we created a <a href="http://www.google.com/search?q=half+edge">half-edge structure</a> of the world mesh. Then when we query the structure we can get in constant time the neighboring edges to the player (typically the <a href="http://bluntobject.wordpress.com/2007/03/13/mesh-data-structures-vol-2-vertex-one-rings/">one-ring of both vertices</a> of the current edge he&#8217;s standing on). We only have to choose from the list of edges which edge the player should go to, and where on that edge he will &#8220;land&#8221;. This is done by choosing the edge with the closest point to the approximation of the players next location.<br />
Let me explain: </p>
<ul>
<li>The player has a certain speed, meaning it can cover a certain distance in a given time. </li>
<li>The approximate next location of the player is the distance it should cover in minimal time. We took a constant value.</li>
<li>Once you have a approximate location, find the edge (and a point on that edge) that best conforms with this location</li>
</ul>
<p><a href="http://www.morethantechnical.com/wp-content/uploads/2009/07/snail_approx_next_location.PNG" rel="lightbox[325]"><img src="http://www.morethantechnical.com/wp-content/uploads/2009/07/snail_approx_next_location-300x222.PNG" alt="approx next location" title="approx next location" width="300" height="222" class="alignleft size-medium wp-image-355" /></a><br />
 In the image you see the player object (snail), the ring of edges around the player that we check (green), the approximate next location (yellow), and the calculated next location (purple).</p>
<p>To find a best location for the object we calculated the distance between the player and the approximate. We ported another piece of <a href="http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm">C code to measure distance between segments</a> to do that:</p>
<pre class="brush: plain;">
/**
	 * find the distance between 2 line segments
	 * @param S1P1 1st point of segment 1
	 * @param S1P0 2nd pt of seg 1
	 * @param S2P1 1st pt of seg 2
	 * @param S2P0 2nd pt of seg 2
	 * @return
	 */
public static Vector3D dist3D_Segment_to_Segment( Vector3D S1P1, Vector3D S1P0, Vector3D S2P1, Vector3D S2P0)
	{
	    Vector3D   u = S1P1.minus(S1P0);
	    Vector3D   v = S2P1.minus(S2P0);
	    Vector3D   w = S1P0.minus(S2P0);
	    float    a = u.innerProduct(u);        // always &gt;= 0
	    float    b = u.innerProduct(v);
	    float    c = v.innerProduct(v);        // always &gt;= 0
	    float    d = u.innerProduct(w);
	    float    e = v.innerProduct(w);
	    float    D = a*c - b*b;       // always &gt;= 0
	    float    sc, sN, sD = D;      // sc = sN / sD, default sD = D &gt;= 0

	    // compute the line parameters of the two closest points
	        sN = (b*e - c*d);
	        if (sN &lt; 0f) {       // sc &lt; 0 =&gt; the s=0 edge is visible
	            return null;
	        }
	        else if (sN &gt; sD) {  // sc &gt; 1 =&gt; the s=1 edge is visible
	            return null;
	        }

	    // finally do the division to get sc
	    sc = (Math.abs(sN) &lt; Vector3D.EPSILON ? 0f : sN / sD);

	    return S1P0.add(u.multiply(sc)); // return the intersection point
	}
</pre>
<p>We actually check what is the intersection point of the approximate edge with the line that goes from the player and in the forward direction. This is the point the player should be if it walked from his current position to that edge.</p>
<p>Orchestrating:</p>
<pre class="brush: plain;">
	/**
	 * find the location where the player should be in the next frame
	 * @param distanceToMoveInThisInterval the distance the player object should (try to) move
	 * @return
	 */
	public EdgeAndIntersectionPointAndDistance findNextLocation(float distanceToMoveInThisInterval) {
		//find all adjacent edges using half-edge struct
		Edge currentEdge = model.getCurrentEdge();
		Set&lt;Edge&gt; allEdges = currentEdge.b.get2RingOfEdges();
		allEdges.addAll(currentEdge.a.get2RingOfEdges());

//A point that lies a little bit along the line from the player and in the forward direction
		Vector3D locationAndSome = location.add(dirForward.multiply(0.05f));

		//find the edge that the player direction is intersecting
		allEdges.remove(currentEdge);
		ArrayList&lt;EdgeAndIntersectionPointAndDistance&gt; intersectingEdges = new ArrayList&lt;EdgeAndIntersectionPointAndDistance&gt;();
		for (Edge e : allEdges) {
			//skip the opposite edge too.
			if((e.a == currentEdge.a &amp;&amp; e.b == currentEdge.b) ||
					(e.b == currentEdge.a &amp;&amp; e.a == currentEdge.b))
				continue;

			Vector3D intPt = Utils.dist3D_Segment_to_Segment(e.a.getVector3D(), e.b.getVector3D(), model.getLocation(), locationAndSome);
			if(intPt != null) {
				float dist = intPt.minus(locationAndSome).getNorm();
				intersectingEdges.add(
					new EdgeAndIntersectionPointAndDistance(e,dist,intPt)
					);
			}
		}

		EdgeAndIntersectionPointAndDistance bestEdge = null;
		float bestDistance = Float.MAX_VALUE;
		for (EdgeAndIntersectionPointAndDistance ead : intersectingEdges) {
			float absD = ead.distance;
			if(absD &lt; bestDistance) {
				bestDistance = absD;
				bestEdge = ead;
			}
		}

		//find the location on that edge where the player should be
		return bestEdge;
	}
</pre>
<p><a href="http://www.morethantechnical.com/wp-content/uploads/2009/07/object_mesh_direction.png" rel="lightbox[325]"><img src="http://www.morethantechnical.com/wp-content/uploads/2009/07/object_mesh_direction-300x139.png" alt="object_mesh_direction" title="object_mesh_direction" width="300" height="139" class="alignleft size-medium wp-image-361" /></a><br />
Of course when the player gets to his next location, he needs to get the correct up-direction and forward direction. This can be taken from the next edge&#8217;s vertices that hold normals.</p>
<p>There are some additional issues to traversing the mesh:</p>
<ul>
<li>Avoiding hitting other players. Done by not allowing 2 player to inhabit the same edge.</li>
<li>Collecting bonuses that are on the ground by checking if the player had walked over (or close to) an edge that has a bonus on it</li>
<li>Up keeping the half-edge structure by updating the edges if they have players/bonuses on them</li>
</ul>
<h2>Make a dent in the world</h2>
<p>One more interesting issue is the missiles impact on the world. Naturally, we&#8217;d like the missiles that hit the ground to make a dent in the world. Our solution was to take the 1-ring of the hit vertex, and lower all the vertices on the ring in the opposite direction to their normals.<br />
<a href="http://www.morethantechnical.com/wp-content/uploads/2009/07/dent_in_mesh.PNG" rel="lightbox[325]"><img src="http://www.morethantechnical.com/wp-content/uploads/2009/07/dent_in_mesh-150x150.PNG" alt="dent_in_mesh" title="dent_in_mesh" width="150" height="150" class="alignnone size-thumbnail wp-image-363" /></a><a href="http://www.morethantechnical.com/wp-content/uploads/2009/07/dent_diagram.png" rel="lightbox[325]"><img src="http://www.morethantechnical.com/wp-content/uploads/2009/07/dent_diagram-300x120.png" alt="dent_diagram" title="dent_diagram" width="300" height="120" class="alignleft size-medium wp-image-372" /></a></p>
<pre class="brush: plain;">
	public static void makeADentInTheMesh(Vertex vtx, float amount, IWorldMeshHandler worldMeshHandler) {
		//Make a dent in the mesh...
		Set&lt;Edge&gt; es = vtx.get2RingOfEdges();
		for (Edge ed : es) {
			Vector3D v = new Vector3D(ed.a.x,ed.a.y,ed.a.z);
			v = v.minus(ed.a.getNormal().multiply(amount));
			ed.a.x = v.x; ed.a.y = v.y; ed.a.z = v.z;
			v = new Vector3D(ed.b.x,ed.b.y,ed.b.z);
			v = v.minus(ed.b.getNormal().multiply(amount));
			ed.b.x = v.x; ed.b.y = v.y; ed.b.z = v.z;

			//If there's stuff on the edge, move it accordingly
			if(ed.playerOnEdge != null) {
				ed.playerOnEdge.getModel().fixLocation();
			}
			if(ed.packageOnEdge != null) {
				Utils.fixObjLocationToEdge(ed.packageOnEdge.getModel(), ed);
			}
			if(ed.treeOnEdge != null) {
				Utils.fixObjLocationToEdge(ed.treeOnEdge.getModel(), ed);
			}
		}

		//Recalculate normals - the positions have changed, creating new &quot;up&quot; directions
		for (Edge ed : es) {
			ed.a.calcNormal();
			ed.b.calcNormal();
		}

		//reset the display list of the world mesh since the vertices and faces have changed
		worldMeshHandler.getWorld().getRenderer().setResetList();
	}
</pre>
<h2>if pigs (missiles) could fly&#8230;</h2>
<p>So far I&#8217;ve covered mesh oriented movement. Missiles, however, are not mesh-bound &#8211; they fly around &#8220;freely&#8221; above the mesh. To imitate gravity and a &#8220;steep course of flight&#8221; for the missiles, we use <a href="http://en.wikipedia.org/wiki/B%C3%A9zier_curve">Bezier curves</a> of either 3 or 4 keypoints.<br />
To calc a point on the curve all you need to know is the current time of flight.</p>
<pre class="brush: plain;">
	protected Vector3D[] mBezierMultiplyV0_V3 = null;

	protected Vector3D V0;
	protected Vector3D V1;
	protected Vector3D V2;
	protected Vector3D V3;

	protected void calcExpectedFlyTime() {
		float distToMove = V3.minus(V0).getNorm(); //total distance to cover
		expectedFlyTime = distToMove / flySpeed;  //approximate time of flight
	}

	protected Vector3D getCurrentCurveLocation(float u) {
		Vector3D out = null;
		out = mBezierMultiplyV0_V3[0].multiply(u * u * u);
		out = out.add(mBezierMultiplyV0_V3[1].multiply(u * u));
		out = out.add(mBezierMultiplyV0_V3[2].multiply(u));
		out = out.add(mBezierMultiplyV0_V3[3]);

		return out;
	}

	private void advanceMissile() {
		float u = timeFromLaunch / expectedFlyTime;
		Vector3D newLocation = this.getCurrentCurveLocation(u);
		this.location = newLocation;

		//calc the change of angle
		this.dirForward = newLocation.minus(this.prevLocation).getNormalized();
		this.dirUp = ((this.dirForward).crossProduct(this.dirLeft)).getNormalized(); //cross-product of left and forward is the new up direction

		this.prevLocation = newLocation;
	}
</pre>
<h2>Get ready for impact!</h2>
<p><a href="http://www.morethantechnical.com/wp-content/uploads/2009/07/missile_hit.png" rel="lightbox[325]"><img src="http://www.morethantechnical.com/wp-content/uploads/2009/07/missile_hit-300x155.png" alt="missile_hit" title="missile_hit" width="300" height="155" class="alignleft size-medium wp-image-367" /></a>I&#8217;ve talked about what happens when a missile hits, and about the missile&#8217;s course, but how do we know when the missile hits the ground?<br />
We have implemented this using a KD-tree over all the vertices in the world mesh, to check what is the closest vertex to the missile. When the missile gets close enough we check the dot product of the missile&#8217;s location and the normal of the surface &#8211; when the sign flips the missile hit the ground.</p>
<pre class="brush: plain;">
	protected void checkForHitWithMesh() {
		Vector3D tipLocation = location.add(dirForward.multiply(distToTip));
		double[] key = {tipLocation.x, tipLocation.y, tipLocation.z};

		//check with KD Tree to get the nearest vertex
		Vertex closestVetrex = null;
		try {
			closestVetrex = VertexKdTree.getVertexKdTree().nearest(key);
		} catch (KeySizeException e) {
			e.printStackTrace();
			return;
		}

		//check if missile is lost in space...
		Vector3D closestVertLocationWorld = aimer.getTransformToMeshLocation().transform(closestVetrex.getVector3D());
		if (tipLocation.minus(closestVertLocationWorld).getNorm() &gt; 1) {
			projectileHandler.setCurrentProjectile(null);
			flightFinished = true;
			objNModeHandler.nextMode();
			return;
		}

		//Check if missile is inside ground
		if ((closestVetrex.getNormal()).innerProduct(tipLocation.minus(closestVertLocationWorld)) &lt; 0) {
			flightFinished = true;
			hitMeshAtVertex(closestVetrex);
			projectileHandler.setCurrentProjectile(null);
		}
	}
</pre>
<h2>Particle Shmarticle</h2>
<p><a href="http://www.morethantechnical.com/wp-content/uploads/2009/07/particles_explosion.PNG" rel="lightbox[325]"><img src="http://www.morethantechnical.com/wp-content/uploads/2009/07/particles_explosion-300x221.PNG" alt="particles_explosion" title="particles_explosion" width="300" height="221" class="aligncenter size-medium wp-image-369" /></a><br />
<a href="http://en.wikipedia.org/wiki/Particle_system">Particle systems</a> are a decent way to simulate smoke, fire, water splashes and anything &#8220;particley&#8221;. We ripped some C code from the net (I can&#8217;t remember where, but it was GPLed), again, and ported it to Java.<br />
The only problem is that OpenGL can&#8217;t really display particles, you need something that has some kind of surface, or a line. Other options are drawing GL_POINTs, or GLU spheres, but both options either don&#8217;t look pleasing or are very costly in terms of performance. So we used GL_TRIANGLE_STRIP to draw small rectangles of random sizes as the particles.</p>
<pre class="brush: plain;">
	public void drawParticles(GL gl) {
		gl.glEnable(GL.GL_BLEND);
		gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
		gl.glDisable(GL.GL_LIGHTING);

		for (int i = 0; i &lt; MAX_PARTICLES; i++) {
			// Each particle is handled differently depending on whether it's
			// alive or not.
			Particle particle = m_aParticles[i];
			if (particle.isAlive()) {
				// This particular particle is alive.
				handleLiveParticle(gl, particle);
			} else {
				// This particular particle is dead.
				handleDeadParticle(gl, particle);
			}
		}
		gl.glDisable(GL.GL_BLEND);
		gl.glEnable(GL.GL_LIGHTING);
	}

	private void handleLiveParticle(GL gl, Particle particle) {
		// The current location of the particle; Need to account for the
		// zoom
		// distance so user can zoom in and out the particles.
		float x = particle.getXLocation();
		float y = particle.getYLocation();
		float z = particle.getZLocation();

		// Set the color to draw this particle. The particle's life value
		// will act as the alpha.
		gl.glColor4f(particle.getRed(), particle.getGreen(),
				particle.getBlue(), particle.getLife());
		// Draw the particle using triangle strips.
		gl.glBegin(GL.GL_TRIANGLE_STRIP);
		// Map the texture and create the vertices for the particle.
		float pSize = (float) Math.random() * particle.getLife();
		float r = (float) Math.random() - pSize;
		gl.glVertex3f(x + pSize, y + pSize, z + r);
		gl.glVertex3f(x - pSize, y + pSize, z + r);
		gl.glVertex3f(x + pSize, y - pSize, z - r);
		gl.glVertex3f(x - pSize, y - pSize, z - r);
		gl.glEnd();

		// Update the particles' properties.
		updateParticle(particle);
	}
</pre>
<p>Note that we make sure the &#8220;particles&#8221; are blended into the buffer, as particles&#8217; color is fading as they near death.</p>
<h2>Sum up</h2>
<p>OK, I&#8217;ve tried to share most of the interesting points in the making of the game. The code is downloadable from the <a href="http://code.google.com/p/taucomputergraphics09/">Google code SVN repo</a>. And here&#8217;s a short video explaining some aspects of playing the game:<br />
<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/XbQ0Qd3gHZM&#038;hl=en&#038;fs=1&#038;"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/XbQ0Qd3gHZM&#038;hl=en&#038;fs=1&#038;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object></p>
<p>Thanks for tuning in!<br />
Roy.</p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fwww.morethantechnical.com%2F2009%2F07%2F27%2Fadvanced-issues-in-3d-game-building-with-jogl-openglswt-w-code-video%2F&amp;linkname=Advanced%20topics%20in%203D%20game%20building%20%5Bw%2F%20code%2C%20video%5D"><img src="http://www.morethantechnical.com/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share/Bookmark"/></a> </p>]]></content:encoded>
			<wfw:commentRss>http://www.morethantechnical.com/2009/07/27/advanced-issues-in-3d-game-building-with-jogl-openglswt-w-code-video/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Combining Java&#8217;s BufferedImage and OpenCV&#8217;s IplImage</title>
		<link>http://www.morethantechnical.com/2009/05/14/combining-javas-bufferedimage-and-opencvs-iplimage/</link>
		<comments>http://www.morethantechnical.com/2009/05/14/combining-javas-bufferedimage-and-opencvs-iplimage/#comments</comments>
		<pubDate>Thu, 14 May 2009 10:51:44 +0000</pubDate>
		<dc:creator>Roy</dc:creator>
				<category><![CDATA[graphics]]></category>
		<category><![CDATA[gui]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[work]]></category>
		<category><![CDATA[bufferedimage]]></category>
		<category><![CDATA[iplimage]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[opencv]]></category>

		<guid isPermaLink="false">http://www.morethantechnical.com/?p=263</guid>
		<description><![CDATA[Hi I recently did a small project combining a Java web service with a OpenCV processing. I tried to transfer the picture from Java environment (as BufferedImage) to OpenCV (IplImage) as seamlessly as possible. This proved a but tricky, especially the Java part where you need to create your own buffer for the image, but [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignleft size-medium wp-image-270" title="java_opencv_img" src="http://www.morethantechnical.com/wp-content/uploads/2009/05/java_opencv_img-300x112.png" alt="java_opencv_img" width="300" height="112" />Hi</p>
<p>I recently did a small project combining a Java web service with a OpenCV processing. I tried to transfer the picture from Java environment (as BufferedImage) to OpenCV (IplImage) as seamlessly as possible. This proved a but tricky, especially the Java part where you need to create your own buffer for the image, but it worked out nicely.</p>
<p>Let me show you how I did it</p>
<p><span id="more-263"></span>First up was creating a 3-component RGB BufferedImage in Java, with a custom memory buffer that will fit nicely with IplImages:</p>
<pre class="brush: plain;">

private BufferedImage createOpenCVCompatibleBufferedImage(int w, int h)
{
ComponentColorModel cm = new ComponentColorModel(
ColorSpace.getInstance(ColorSpace.CS_sRGB),
false,  //no alpha channel
false,  //not premultiplied
ColorModel.OPAQUE,
DataBuffer.TYPE_BYTE); //important - data in the buffer is saved by the byte

SampleModel sm = cm.createCompatibleSampleModel(w, h);
DataBufferByte db = new DataBufferByte(w*h*3); //3 channels buffer
WritableRaster r = WritableRaster.createWritableRaster(sm, db, new Point(0,0));
BufferedImage bm = new BufferedImage(cm,r,false,null);
return bm;
}
</pre>
<p>This took some trial and error, but I ended up with a working thing.</p>
<p>Now, the C++/OpenCV side of things &#8211; a JNI function to read the buffer and do some OpenCV stuff:</p>
<pre class="brush: plain;">

JNIEXPORT jint JNICALL Java_test_OpenCVShowImage
(JNIEnv *env, jobject jo, jbyteArray pic, jint w, jint h, jint bpp, jint bpr) {
IplImage* img;
jint len;
unsigned char* result;
int n1;

img = cvCreateImageHeader(cvSize(w,h),8,bpp/8); //create the &quot;shell&quot;

len = (*env)-&gt;GetArrayLength(env, pic);
result = (unsigned char *)malloc(len + 1);
if (result == 0) {
fatal_error(&quot;out of memory&quot;);
(*env)-&gt;DeleteLocalRef(env, pic);
return 0;
}
(*env)-&gt;GetByteArrayRegion(env, pic, 0, len,(jbyte *)result);

cvSetData(img,result,bpr);    //set the buffer

cvNamedWindow(&quot;window&quot;);
cvShowImage(&quot;window&quot;,img);
cvWaitKey(0);
cvDestroyWindow(&quot;window&quot;);

free(result);
cvReleaseImage(&amp;img);
return 1;
}
</pre>
<p>One last thing -the Java call to the JNI function:</p>
<pre class="brush: plain;">
BufferedImage tmp = ImageIO.read(new File(&quot;bird.png&quot;));

BufferedImage bi1 = createOpenCVCompatibleBufferedImage(tmp.getWidth(), tmp.getHeight());

//paint the image with a little transform...
Graphics2D biDestG2D = bi1.createGraphics();
biDestG2D.setColor(Color.white);
biDestG2D.fillRect(0, 0, w, h);
AffineTransform transform = AffineTransform.getShearInstance(0.2, 0.1);
transform.scale(0.5, 0.5);
biDestG2D.drawImage(tmp,transform,null);
biDestG2D.dispose();

byte[] bytes = ((DataBufferByte)bi1.getRaster().getDataBuffer()).getData();

OpenCVShowImage(bytes, w, h, 24, w*3);
</pre>
<p>That&#8217;s all folks!<br />
<img class="alignnone size-full wp-image-269" title="java_opencv" src="http://www.morethantechnical.com/wp-content/uploads/2009/05/java_opencv.png" alt="java_opencv" width="642" height="418" /></p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fwww.morethantechnical.com%2F2009%2F05%2F14%2Fcombining-javas-bufferedimage-and-opencvs-iplimage%2F&amp;linkname=Combining%20Java%26%238217%3Bs%20BufferedImage%20and%20OpenCV%26%238217%3Bs%20IplImage"><img src="http://www.morethantechnical.com/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share/Bookmark"/></a> </p>]]></content:encoded>
			<wfw:commentRss>http://www.morethantechnical.com/2009/05/14/combining-javas-bufferedimage-and-opencvs-iplimage/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Qt &amp; OpenCV combined for face detecting QWidgets</title>
		<link>http://www.morethantechnical.com/2009/03/05/qt-opencv-combined-for-face-detecting-qwidgets/</link>
		<comments>http://www.morethantechnical.com/2009/03/05/qt-opencv-combined-for-face-detecting-qwidgets/#comments</comments>
		<pubDate>Thu, 05 Mar 2009 12:19:37 +0000</pubDate>
		<dc:creator>Roy</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[gui]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[qt]]></category>
		<category><![CDATA[video]]></category>
		<category><![CDATA[face detection]]></category>
		<category><![CDATA[opencv]]></category>

		<guid isPermaLink="false">http://www.morethantechnical.com/?p=212</guid>
		<description><![CDATA[As my search for the best platform to roll-out my new face detection concept continues, I decided to give ol&#8217; Qt framework a go. I like Qt. It&#8217;s cross-platform, a clear a nice API, straightforward, and remindes me somewhat of Apple&#8217;s Cocoa. My intention is to get some serious face detection going on mobile devices. [...]]]></description>
			<content:encoded><![CDATA[<p>As my search for the best platform to roll-out my new face detection concept continues, I decided to give ol&#8217; Qt framework a go.</p>
<p>I like Qt. It&#8217;s cross-platform, a clear a nice API, straightforward, and remindes me somewhat of Apple&#8217;s Cocoa.</p>
<p>My intention is to get some serious face detection going on mobile devices. So that means either the iPhone, which so far did a crummy job performance-wise, or some other mobile device, preferably linux-based.<br />
This led me to the decision to go with Qt. I believe you can get it to work on any linux-ish platform (limo, moblin, android), and since Nokia baught Trolltech &#8211; it&#8217;s gonna work on Nokia phones soon, awesome!</p>
<p>Lets get to the details, shall we?<br />
<span id="more-212"></span><br />
First thing&#8217;s first: face detection.</p>
<p>I ripped OpenCV&#8217;s facedetect.c sample and extracted only the detect_and_draw() function. Originally the function detects the faces and draws a circle over them, but I needed only the face detection and the result bounding rectangle. So in the end I was left with this:</p>
<pre class="brush: cpp;">

CvRect detect_and_draw( IplImage* img, CvMemStorage* storage, CvHaarClassifierCascade* cascade )
{
IplImage *gray, *small_img;
int i = 0;

gray = cvCreateImage( cvSize(img-&gt;width,img-&gt;height), 8, 1 );
small_img = cvCreateImage( cvSize( cvRound (img-&gt;width/scale),
cvRound (img-&gt;height/scale)), 8, 1 );

cvCvtColor( img, gray, CV_RGB2GRAY );
cvResize( gray, small_img, CV_INTER_LINEAR );
cvEqualizeHist( small_img, small_img );
cvClearMemStorage( storage );

CvRect* r = NULL;

if( cascade )
{
double t = (double)cvGetTickCount();
CvSeq* faces = cvHaarDetectObjects( small_img, cascade, storage,
1.1, 2, 0
|CV_HAAR_FIND_BIGGEST_OBJECT
//|CV_HAAR_DO_ROUGH_SEARCH
//|CV_HAAR_DO_CANNY_PRUNING
//|CV_HAAR_SCALE_IMAGE
,
cvSize(30, 30) );
t = (double)cvGetTickCount() - t;

printf( &quot;detection time = %gms\n&quot;, t/((double)cvGetTickFrequency()*1000.) );

r = (CvRect*)cvGetSeqElem( faces, i );

cvReleaseImage( &amp;amp;amp;amp;amp;amp;gray );
cvReleaseImage( &amp;amp;amp;amp;amp;amp;small_img );

if(r) {
return cvRect(r-&gt;x,r-&gt;y,r-&gt;width,r-&gt;height);
} else {
return cvRect(-1,-1,0,0);
}

}
</pre>
<p>This can go anywhere in the code base, as it&#8217;s totally independant (as long as you train the cascade and allocate a MemStorage).Note that I am assuming only one face in the input image, and also that it will be the largest detected object. This bring my benchmark to about 25ms per frame, using the original general detection approach of facedetect.c benchmarked at about 160ms per frame.</p>
<p>OK, done with pure OpenCV, on to Qt.</p>
<p>I subclassed a QWidget, who&#8217;s sole purpose is to show the input video with the detected face. For starters, I needed to have a QImage and an IplImage instances as members, they can also share the same buffer (how awesome is that..). I also need a CvCapture, CvMemStorage and a CvHaarCalssifierCascade:</p>
<pre class="brush: cpp;">

class FaceRecognizer : public QWidget
{
Q_OBJECT

public:
FaceRecognizer(QWidget *parent = 0);
~FaceRecognizer();

private:
Ui::FaceRecognizerClass ui;

QImage m_i;

QRect faceLoc;

CvMemStorage* storage;
CvHaarClassifierCascade* cascade;
CvCapture* capture;
IplImage* m_opencvimg;

QTimer* m_timer;

void paintEvent(QPaintEvent* e);

public slots:
void queryFrame();
};
</pre>
<p>You can see that I&#8217;m gonna use a QTimer to query the frames from the CvCapture and also I override paintEvent to paint the frame onto the canvas. In fact in my QWidget I have a QFrame, that the image will painted over it. UI was generated in Qt Designer.</p>
<p>First, some initialization in the constructor:</p>
<pre class="brush: cpp;">

FaceRecognizer::FaceRecognizer(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);

capture = cvCaptureFromAVI( &quot;/home/user/Desktop/video.avi&quot; );
//grab one frame to get width and height

IplImage* frame = cvQueryFrame( capture );

m_i = QImage(QSize(frame-&gt;width,frame-&gt;height),QImage::Format_RGB888);
ui.frame-&gt;setMinimumSize(m_i.width(),m_i.height());
ui.frame-&gt;setMaximumSize(ui.frame-&gt;minimumSize());
//create only the header, as the data buffer is shared, and was allocated by QImage

m_opencvimg = cvCreateImageHeader(cvSize(m_i.width(),m_i.height()),8,3);
m_opencvimg-&gt;imageData = (char*)m_i.bits(); // share buffers

if( frame-&gt;origin == IPL_ORIGIN_TL )
cvCopy( frame, m_opencvimg, 0 );
else
cvFlip( frame, m_opencvimg, 0 );

//images from cvQueryFrame come in BGR form and not what Qt expects - RGB

//and since the buffers are shared - format should be consistent
cvCvtColor(m_opencvimg,m_opencvimg,CV_BGR2RGB);

//we need memstorage and a cascade
storage = cvCreateMemStorage(0);
cascade = (CvHaarClassifierCascade*)cvLoad( CASCADE_NAME, 0, 0, 0 );

//set timer for 50ms intervals

m_timer = new QTimer(this);
connect(m_timer,SIGNAL(timeout()),this,SLOT(queryFrame()));
m_timer-&gt;start(50);
}
</pre>
<p>And now, querying the frame: query CvCapture, convert BGR to RGB, detect faces and update faceLoc QRect.</p>
<pre class="brush: cpp;">

void FaceRecognizer::queryFrame() {
IplImage* frame = cvQueryFrame( capture );

if( frame-&gt;origin == IPL_ORIGIN_TL )
cvCopy( frame, m_opencvimg, 0 );
else
cvFlip( frame, m_opencvimg, 0 );
cvCvtColor(m_opencvimg,m_opencvimg,CV_BGR2RGB);

CvRect r = detect_and_draw(m_opencvimg,storage,cascade);
faceLoc = QRect(QPoint(r.x,r.y),QSize(r.width,r.height));

this-&gt;update();
}

Finally - painting, which is easy:

void FaceRecognizer::paintEvent(QPaintEvent* e) {
QPainter painter(this);

painter.drawImage(QPoint(ui.frame-&gt;x(),ui.frame-&gt;y()),m_i);

if(faceLoc.x() &gt; 0 &amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp; faceLoc.y() &gt; 0) {
painter.setBrush(Qt::NoBrush);
painter.setPen(QColor(255,0,0));
painter.drawRect(QRect(faceLoc.x()+ui.frame-&gt;x(),faceLoc.y()+ui.frame-&gt;y(),faceLoc.width(),faceLoc.height()));
}
}
</pre>
<p>Looks like it&#8217;s all done&#8230; Here&#8217;s a video:<br />
<object width="425" height="344" data="http://www.youtube.com/v/3eyh-jmAQR0&amp;hl=en&amp;fs=1" type="application/x-shockwave-flash"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/3eyh-jmAQR0&amp;hl=en&amp;fs=1" /><param name="allowfullscreen" value="true" /></object></p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fwww.morethantechnical.com%2F2009%2F03%2F05%2Fqt-opencv-combined-for-face-detecting-qwidgets%2F&amp;linkname=Qt%20%26%23038%3B%20OpenCV%20combined%20for%20face%20detecting%20QWidgets"><img src="http://www.morethantechnical.com/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share/Bookmark"/></a> </p>]]></content:encoded>
			<wfw:commentRss>http://www.morethantechnical.com/2009/03/05/qt-opencv-combined-for-face-detecting-qwidgets/feed/</wfw:commentRss>
		<slash:comments>22</slash:comments>
		</item>
		<item>
		<title>Showing video with Qt toolbox and ffmpeg libraries</title>
		<link>http://www.morethantechnical.com/2008/11/02/showing-video-with-qt-toolbox-and-ffmpeg-libraries/</link>
		<comments>http://www.morethantechnical.com/2008/11/02/showing-video-with-qt-toolbox-and-ffmpeg-libraries/#comments</comments>
		<pubDate>Sun, 02 Nov 2008 08:12:00 +0000</pubDate>
		<dc:creator>Roy</dc:creator>
				<category><![CDATA[ffmpeg]]></category>
		<category><![CDATA[graphics]]></category>
		<category><![CDATA[gui]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[qt]]></category>
		<category><![CDATA[video]]></category>
		<category><![CDATA[sdl]]></category>

		<guid isPermaLink="false">http://www.morethantechnical.com/?p=78</guid>
		<description><![CDATA[I recently had to build a demo client that shows short video messages for Ubuntu environment. After checking out GTK+ I decided to go with the more natively OOP Qt toolbox (GTKmm didn&#8217;t look right to me), and I think i made the right choice. So anyway, I have my video files encoded in some [...]]]></description>
			<content:encoded><![CDATA[<p>I recently had to build a demo client that shows short video messages for Ubuntu environment.<br />
After checking out GTK+ I decided to go with the more natively OOP Qt toolbox (GTKmm didn&#8217;t look right to me), and I think i made the right choice.</p>
<p>So anyway, I have my video files encoded in some unknown format and I need my program to show them in a some widget. I went around looking for an exiting example, but i couldn&#8217;t find anything concrete, except for a good tip <a href="http://lists.trolltech.com/qt-interest/2007-09/thread00806-0.html">here</a> that led me <a href="http://www.inb.uni-luebeck.de/%7Eboehme/libavcodec_update.html">here</a> for an example of using ffmpeg&#8217;s libavformat and libavcodec, but no end-to-end example including the Qt code.</p>
<p>The ffmpeg example was simple enough to just copy-paste into my project, but the whole painting over the widget&#8217;s canvas was not covered. Turns out painting video is not as simple as overriding paintEvent()&#8230;</p>
<p>Firstly, you need a separate thread for grabbing frames from the video file, because you won&#8217;t let the GUI event thread do that.<br />
That makes sense, but when the frame-grabbing thread (I called VideoThread) actually grabbed a frame and inserted it somewhere in the memory, I needed to tell the GUI thread to take that buffered pixels and paint them over the widget&#8217;s canvas.</p>
<p>This is the moment where I praise Qt&#8217;s excellent Signals/Slots mechanism. So I&#8217;ll have my VideoThread emit a signal notifying some external entity that a new frame is in the buffer.<br />
Here&#8217;s a little code:</p>
<p><span id="more-78"></span></p>
<pre class="brush: plain;">
void VideoThread::run() {
/*
... Initialize libavformat &amp;amp;amp; libavcodec data structures.
You can see it in the example i referred to before
*/
// Open video file
if(av_open_input_file(&amp;amp;amp;pFormatCtx,
&amp;quot;lala.avi&amp;quot;,
NULL, 0, NULL)!=0)
return -1; // Couldn't open file

// Retrieve stream information
if(av_find_stream_info(pFormatCtx)&amp;amp;lt;0)
return -1; // Couldn't find stream information

// Find the first video stream ...

// Get a pointer to the codec context for the video
// stream...

// Find the decoder for the video stream...

// Open codec...

// Allocate video frame
pFrame=avcodec_alloc_frame();

// Allocate an AVFrame structure
pFrameRGB=avcodec_alloc_frame();
if(pFrameRGB==NULL)
return -1;

int dst_fmt = PIX_FMT_RGB24;
int dst_w = 160;
int dst_h = 120;

// Determine required buffer size and allocate buffer
numBytes = avpicture_get_size(dst_fmt, dst_w, dst_h);
buffer = new uint8_t[numBytes + 64];

//put a PPM header on the buffer
int headerlen = sprintf((char *) buffer,
&amp;quot;P6n%d %dn255n&amp;quot;,
dst_w, dst_h);

_v-&amp;amp;gt;buf = (uchar*)buffer;
_v-&amp;amp;gt;len = avpicture_get_size(dst_fmt,dst_w,dst_h) +
headerlen;

// Assign appropriate parts of buffer to image planes
// in pFrameRGB...

// I use libswscale to scale the frames to the required
// size.
// Setup the scaling context:
SwsContext *img_convert_ctx;
img_convert_ctx = sws_getContext(
pCodecCtx-&amp;amp;gt;width, pCodecCtx-&amp;amp;gt;height,
pCodecCtx-&amp;amp;gt;pix_fmt,
dst_w, dst_h, dst_fmt,
SWS_BICUBIC, NULL, NULL, NULL);

// Read frames and notify
i=0;
while(av_read_frame(pFormatCtx, &amp;amp;amp;packet)&amp;amp;gt;=0)
{
// Is this a packet from the video stream?
if(packet.stream_index==videoStream)
{
// Decode video frame
avcodec_decode_video(pCodecCtx,
pFrame,
&amp;amp;amp;frameFinished,
packet.data,
packet.size);

// Did we get a video frame?
if(frameFinished)
{
// Convert the image to RGB
sws_scale(img_convert_ctx,
pFrame-&amp;amp;gt;data,
pFrame-&amp;amp;gt;linesize,
0,
pCodecCtx-&amp;amp;gt;height,
pFrameRGB-&amp;amp;gt;data,
pFrameRGB-&amp;amp;gt;linesize);

emit frameReady();

//My video is 5FPS so sleep for 200ms.
this-&amp;amp;gt;msleep(200);
}
}

// Free the packet that was allocated by
// av_read_frame
av_free_packet(&amp;amp;amp;packet);
}

// Free the RGB image
delete [] buffer;
av_free(pFrameRGB);

// Free the YUV frame
av_free(pFrame);

// Close the codec...

// Close the video file...
} //end VideoThread::run
</pre>
<p>Ok so I have a frame-grabber that emits a frameReady signal everytime the buffer is full and ready for painting.</p>
<p>A couple of things to notice:</p>
<ul style="text-align: left;">
<li>I convert the image format to PIX_FMT_RGB24 (avcodec.h), which is required by Qt&#8217;s QImage::fromData() method.</li>
<li>I scale the image using ffmpeg&#8217;s libswscale. All conversion/scaling methods inside libavcodev are deprecated now.<br />
But it&#8217;s fairly simple, <a href="http://www.dranger.com/ffmpeg/tutorial08.html">here</a>&#8216;s a good example. Just remember you need a sws_getContext and then sws_scale.</li>
<li>I totally disregard actual frame rate here, I just sleep for 200ms because i know my file is 5FPS. For a (far-) more sophisticated way to get the FPS, very important if this is not a constant frame-rate video, you can find <a href="http://www.dranger.com/ffmpeg/tutorial05.html">here</a>.</li>
<li>I don&#8217;t cover audio in this example, although the mechanism to extract it from the file exists&#8230; you just need to grabe the audio stream&#8217;s frame. For playing audio you also need some Qt-external library. In a different project I used SDL very easily, <a href="http://www.dranger.com/ffmpeg/tutorial03.html">here</a>&#8216;s an example online.</li>
</ul>
<p>Now, for painting over the widget.<br />
This is fairly easy:</p>
<pre class="brush: plain;">
void VideoWidget::paintEvent(QPaintEvent * e) {
QPainter painter(this);
if(buf) {
QImage i = QImage::fromData(buf,len,&amp;quot;PPM&amp;quot;);
painter.drawImage(QPoint(0,0),i);
}
}
</pre>
<p>Two things to note:</p>
<ul style="text-align: left;">
<li>The widget needs to be given the pointer to the video frame buffer (buf).</li>
<li>The frame buffer needs to be in a PPM format. That means it needs to get a PPM header, which looks something like this: &#8220;P6n320 240n255n&#8221;, and then all the pixels in 3-byte per-pixel format (RGB24). You can see that i take care of that in the previous code block.</li>
</ul>
<p>Finally we need to orchestrate this whole mess.<br />
So in my GUI-screen class I do:</p>
<pre class="brush: plain;">
....
vt = new VideoThread();
connect(vt,SIGNAL(frameReady()),this,SLOT(updateVideoWidget()));
vt-&amp;amp;gt;start();
....
</pre>
<p>And:</p>
<pre class="brush: plain;">
void playMessage::updateVideoWidget() {
videoWidget-&amp;amp;gt;repaint(); //or update().
}
</pre>
<p>This will make the widget repaint on each frame ready.</p>
<p>Note:</p>
<ul style="text-align: left;">
<li>In this example I don&#8217;t take care of multi-threading issues. Since the GUI and the ffmpeg decoder threads share a memory buffer, I should probably have a mutex to protect it. It&#8217;s a classic producer-consumer problem.</li>
<li>Performance wise, Qt&#8217;s paint mechanism is by far the worst way to go when displaying video&#8230; but it&#8217;s great for a quick-and-dirty solution (I only needed 5fps). A more performance favorable solution will probably be using an overlay block and frame-serving with SDL.</li>
</ul>
<p>Enjoy!<br />
Roy.</p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fwww.morethantechnical.com%2F2008%2F11%2F02%2Fshowing-video-with-qt-toolbox-and-ffmpeg-libraries%2F&amp;linkname=Showing%20video%20with%20Qt%20toolbox%20and%20ffmpeg%20libraries"><img src="http://www.morethantechnical.com/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share/Bookmark"/></a> </p>]]></content:encoded>
			<wfw:commentRss>http://www.morethantechnical.com/2008/11/02/showing-video-with-qt-toolbox-and-ffmpeg-libraries/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
