<?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>my tech blog &#187; Signal Processing</title>
	<atom:link href="http://billauer.se/blog/category/signal/feed/" rel="self" type="application/rss+xml" />
	<link>https://billauer.se/blog</link>
	<description>Anything I found worthy to write down.</description>
	<lastBuildDate>Thu, 12 Mar 2026 11:36:00 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.2</generator>
		<item>
		<title>Using ImageMagick to convert a 4:2:2 YCrCb raw image to something viewable</title>
		<link>https://billauer.se/blog/2009/02/imagemagick-convert-ycbcr/</link>
		<comments>https://billauer.se/blog/2009/02/imagemagick-convert-ycbcr/#comments</comments>
		<pubDate>Sat, 28 Feb 2009 16:37:41 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Signal Processing]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[ImageMagick YCbCr YUV 4:2:2]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=9</guid>
		<description><![CDATA[Some electronic imaging system dumped a 4:2:2 YCbCr raw image into a file. It&#8217;s a debug output. Now I wanted to see this image. GIMP doesn&#8217;t import that format. ImageMagick had the solution. It was as simple as convert -size 800x600 pal:ImageOut.raw ImageOut.bmp I don&#8217;t know why they picked the codename &#8220;pal&#8221; to represent 4:2:2 [...]]]></description>
			<content:encoded><![CDATA[<p>Some electronic imaging system dumped a 4:2:2 YCbCr raw image into a file. It&#8217;s a debug output. Now I wanted to see this image. GIMP doesn&#8217;t import that format. ImageMagick had the solution. It was as simple as</p>
<pre>convert -size 800x600 pal:ImageOut.raw ImageOut.bmp</pre>
<p>I don&#8217;t know why they picked the codename &#8220;pal&#8221; to represent 4:2:2 format (maybe because of ITU-R BT.601?) , but it did the job. Ah, they call it &#8220;YUV&#8221; and not &#8220;YCbCr&#8221;, but it was really close enough for me.</p>
<p>For a list of supported formats:</p>
<pre>convert -list format</pre>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2009/02/imagemagick-convert-ycbcr/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>peakdet: Peak detection using MATLAB (non-derivative local extremum, maximum, minimum)</title>
		<link>https://billauer.se/blog/2009/01/peakdet-matlab-octave/</link>
		<comments>https://billauer.se/blog/2009/01/peakdet-matlab-octave/#comments</comments>
		<pubDate>Thu, 01 Jan 2009 14:05:07 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[Signal Processing]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=6564</guid>
		<description><![CDATA[Here&#8217;s a problem I encounter in several fields: Find the local maxima and minima in some noisy signal, which typically looks like the following graph: The local maxima and minima are plotted as red and green stars on the graph. To the eye it&#8217;s so obvious where they are, but making a computer find them [...]]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s a problem I encounter in several fields: Find the local maxima and minima in some noisy signal, which typically looks like the following graph:</p>
<p><img class="aligncenter" src="/non-html/matlab_image.gif" alt="peakdet MATLAB plot" width="508" /></p>
<p>The local maxima and minima are plotted as red and green stars on the graph. To the eye it&#8217;s so obvious where they are, but making a computer find them can turn out tricky.</p>
<p>Let&#8217;s start with what <span style="color: red;"><strong>not to do</strong></span>: Using the well-known zero-derivate method. Due to the noise, which is always there in real-life signals, accidental zero-crossings of the first derivate occur, yielding false detections. The typical solution is to smooth the curve with some low-pass filter, usually killing the original signal at the same time. The result is usually that the algorithm goes horribly wrong where it&#8217;s so obvious to the eye.</p>
<p>In many cases, we don&#8217;t really care about maxima and minima in the mathematical sense. We can see the peaks and valleys, and we want the computer to find them. This is what &#8220;peakdet&#8221; does.</p>
<p>The trick here is to realize, that a peak is the highest point betweem &#8220;valleys&#8221;. What makes a peak is the fact that there are lower points around it. This strategy is adopted by &#8220;peakdet&#8221;: Look for the highest point, around which there are points lower by X on both sides.</p>
<p>Let&#8217;s see an example: First, let&#8217;s create the graph shown in the figure above:</p>
<pre>&gt;&gt; t=0:0.001:10;
&gt;&gt; x=0.3*sin(t) + sin(1.3*t) + 0.9*sin(4.2*t) + 0.02*randn(1, 10001);
&gt;&gt; figure; plot(x);</pre>
<p>Now we&#8217;ll find the peaks and valleys: (you&#8217;ll need to copy the &#8220;peakdet&#8221; function from the bottom of this page and put it in your working directory or a directory in the MATLAB search path):</p>
<pre>&gt;&gt; [maxtab, mintab] = peakdet(x, 0.5);
&gt;&gt; hold on; plot(mintab(:,1), mintab(:,2), 'g*');
&gt;&gt; plot(maxtab(:,1), maxtab(:,2), 'r*');</pre>
<p>Note the call to peakdet(): The first argument is the vector to examine, and the second is the peak threshold: We require a difference of at least 0.5 between a peak and its surrounding in order to declare it as a peak. Same goes with valleys.</p>
<p>The returned vectors &#8220;maxtab&#8221; and &#8220;mintab&#8221; contain the peak and valley points, as evident by their plots (note the colors).</p>
<p>The vector&#8217;s X-axis values can be passed as a third argument (thanks to Sven Billiet for his contribution on this), in which case peakdet() returns these values instead of indices, as shown in the following example:</p>
<pre>&gt;&gt; figure; plot(t,x);
&gt;&gt; [maxtab, mintab] = peakdet(x, 0.5, t);</pre>
<p>And from here we continue like before, but note that the X axis represents &#8220;t&#8221; and not indices.</p>
<pre>&gt;&gt; hold on; plot(mintab(:,1), mintab(:,2), 'g*');
&gt;&gt; plot(maxtab(:,1), maxtab(:,2), 'r*');</pre>
<p>As for the implementation of this function: The work is done with a for-loop, which is considered lousy practice in MATLAB. Since I&#8217;ve never needed this function for anything else than pretty short vectors (&lt; 100000 points), I also never bothered to try speeding it up. Compiling to MEX is a direct solution. I&#8217;m not sure if it&#8217;s possible to vectorize this algorithm in MATLAB. I&#8217;ll be glad to hear suggestions.</p>
<p>A final note: If you happen to prefer Python, <a rel="noopener" href="https://gist.github.com/250860" target="_blank"> you could try this</a> (someone has been kind enough to convert this function). There are also a <a rel="noopener" href="https://github.com/xuphys/peakdetect" target="_blank">version in C</a> by Hong Xu and a <a rel="noopener" href="http://andrew.rsmas.miami.edu/bmcnoldy/tmp/signalproc.f90" target="_blank">version in FORTRAN 90</a> by Brian McNoldy. I haven&#8217;t verified any of these.</p>
<p>And here is the function. Copy and save it as &#8216;peakdet.m&#8217;. It&#8217;s released to the public domain:</p>
<pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">[maxtab, mintab]</span>=<span class="hljs-title">peakdet</span><span class="hljs-params">(v, delta, x)</span></span>
<span class="hljs-comment">%PEAKDET Detect peaks in a vector</span>
<span class="hljs-comment">%        [MAXTAB, MINTAB] = PEAKDET(V, DELTA) finds the local</span>
<span class="hljs-comment">%        maxima and minima ("peaks") in the vector V.</span>
<span class="hljs-comment">%        MAXTAB and MINTAB consists of two columns. Column 1</span>
<span class="hljs-comment">%        contains indices in V, and column 2 the found values.</span>
<span class="hljs-comment">%      </span>
<span class="hljs-comment">%        With [MAXTAB, MINTAB] = PEAKDET(V, DELTA, X) the indices</span>
<span class="hljs-comment">%        in MAXTAB and MINTAB are replaced with the corresponding</span>
<span class="hljs-comment">%        X-values.</span>
<span class="hljs-comment">%</span>
<span class="hljs-comment">%        A point is considered a maximum peak if it has the maximal</span>
<span class="hljs-comment">%        value, and was preceded (to the left) by a value lower by</span>
<span class="hljs-comment">%        DELTA.</span>

<span class="hljs-comment">% Eli Billauer, 3.4.05</span>
<span class="hljs-comment">% This function is released to the public domain; Any use is allowed.</span>

maxtab = [];
mintab = [];

v = v(:); <span class="hljs-comment">% Just in case this wasn't a proper vector</span>

<span class="hljs-keyword">if</span> nargin &lt; <span class="hljs-number">3</span>
  x = (<span class="hljs-number">1</span>:<span class="hljs-built_in">length</span>(v))';
<span class="hljs-keyword">else</span>
  x = x(:);
  <span class="hljs-keyword">if</span> <span class="hljs-built_in">length</span>(v)~= <span class="hljs-built_in">length</span>(x)
    error(<span class="hljs-string">'Input vectors v and x must have same length'</span>);
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>

<span class="hljs-keyword">if</span> (<span class="hljs-built_in">length</span>(delta(:)))&gt;<span class="hljs-number">1</span>
  error(<span class="hljs-string">'Input argument DELTA must be a scalar'</span>);
<span class="hljs-keyword">end</span>

<span class="hljs-keyword">if</span> delta &lt;= <span class="hljs-number">0</span>
  error(<span class="hljs-string">'Input argument DELTA must be positive'</span>);
<span class="hljs-keyword">end</span>

mn = Inf; mx = -Inf;
mnpos = NaN; mxpos = NaN;

lookformax = <span class="hljs-number">1</span>;

<span class="hljs-keyword">for</span> <span class="hljs-built_in">i</span>=<span class="hljs-number">1</span>:<span class="hljs-built_in">length</span>(v)
  this = v(<span class="hljs-built_in">i</span>);
  <span class="hljs-keyword">if</span> this &gt; mx, mx = this; mxpos = x(<span class="hljs-built_in">i</span>); <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">if</span> this &lt; mn, mn = this; mnpos = x(<span class="hljs-built_in">i</span>); <span class="hljs-keyword">end</span>

  <span class="hljs-keyword">if</span> lookformax
    <span class="hljs-keyword">if</span> this &lt; mx-delta
      maxtab = [maxtab ; mxpos mx];
      mn = this; mnpos = x(<span class="hljs-built_in">i</span>);
      lookformax = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">else</span>
    <span class="hljs-keyword">if</span> this &gt; mn+delta
      mintab = [mintab ; mnpos mn];
      mx = this; mxpos = x(<span class="hljs-built_in">i</span>);
      lookformax = <span class="hljs-number">1</span>;
    <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span></pre>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2009/01/peakdet-matlab-octave/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>easyspec: A spectrum-analyzer like plotter for MATLAB</title>
		<link>https://billauer.se/blog/2009/01/easyspec-matlab-octave-spectrum-analyzer/</link>
		<comments>https://billauer.se/blog/2009/01/easyspec-matlab-octave-spectrum-analyzer/#comments</comments>
		<pubDate>Thu, 01 Jan 2009 13:59:20 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[Signal Processing]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=6559</guid>
		<description><![CDATA[I&#8217;m using MATLAB since about 1992. One of the things that I&#8217;ve always missed, as a communication engineer, is a quick and dirty spectrum plot of signals. I want to see what the spectrum analyzer will show me when I&#8217;ll run the signal through it, and not some analytic plot, which looks nothing like. The [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m using MATLAB since about 1992. One of the things that I&#8217;ve always missed, as a communication engineer, is a quick and dirty spectrum plot of signals. I want to see what the spectrum analyzer will show me when I&#8217;ll run the signal through it, and not some analytic plot, which looks nothing like.</p>
<p>The &#8220;easyspec&#8221; MATLAB function does exactly that. Using this function on a time signal has the feel of plugging in a spectrum analyzer: You get some simple, concise image (scaled in dBs, for us engineers), which is as reliable as if you measured the signal. Meaning, that if the signal is not stationary (choose whatever sense you want for &#8220;stationary&#8221;) you may want to re-run the function to see if you get the same result.</p>
<p>Let&#8217;s try a simple session (you&#8217;ll need to copy the &#8220;easyspec&#8221; function from the bottom of this page and put it in your working directory or a directory in the MATLAB search path):</p>
<pre>&gt;&gt; t=0:1/44100:10;
&gt;&gt; s=0.25*sin(2*pi*440*t) + sin(2*pi*1000*t) + randn(1,441001);
&gt;&gt; easyspec(s, 44100);</pre>
<p>This shows the spectrum of two tones, one at 440Hz, and another at 1000Hz, sampled at 44.1 kHz. Note that since we hand over the sample rate to easyspec, it will show the real-life frequency on the x axis.</p>
<p>The special thing about this function, is that it chooses the FFT segments at random. As a result, subsequent calls to this function on the same signal will result in slightly differently plots. This is exactly what we expect from a real-life spectrum analyzer.</p>
<p>As given below, this function picks (at random) 100 segments of 4096 samples, applies a Hanning window to each, and pads the vector to a length of 16384. The absolute value of the FFT of this vector is calculated.</p>
<p>What is displayed is the average of these 100 absolute value vectors, normalized so that a sine wave of amplitude 1 will show as <strong>approximately</strong> 0 dB on the spectrum plot. This is in the spirit of the &#8220;video average&#8221; function of common spectrum analyzers, but it works differently (spectrum analyzers usually average on the dB values). Anyhow, this makes a smoother display of truly random signals (such as noise).</p>
<p>The most common question I get is where <strong>the 17.127 constant</strong> came from. The honest answer is that I simply played with the number until I got 0 dB for a unity sine wave. It&#8217;s true that I could have made some calculations to reach a constant with theory behind, but I found it pretty pointless, since the accuracy is compromised anyhow. (The main reason is that the reading depends on the frequency, as each FFT bin&#8217;s response to a pure sine wave is the Hanning window&#8217;s Fourier transform).</p>
<p>The reason for picking the segments at random comes from real-life spectrum analyzers: Almost always, there is no synchronization between the sweep instances and the measured signal. Also, let&#8217;s keep in mind that the term &#8220;spectrum&#8221; comes from the world of probability theory, and it&#8217;s the Fourier Transform of the autocorrelation function of a side-sense stationary random signal. Now we may ask ourselves what meaning there is to a spectrum of a deterministic signal. What&#8217;s random about a plain sine wave that we apply to a spectrum analyzer?</p>
<p>The answer is that the time segment, on which we measure the signal, is random. If it&#8217;s not, as in MATLAB&#8217;s native &#8220;spectrum&#8221; function, a misleading result may be displayed as a result of some relation between the measured signal&#8217;s periodicity and the jumps between the FFT segments. This doesn&#8217;t happen when the signal is simple noise, but as soon as there is anything periodic about it, strange things can happen.</p>
<p>And finally, here is the function in question. Copy and save it as &#8216;easyspec.m&#8217;. It&#8217;s released to the public domain:</p>
<pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">[s,f]</span>=<span class="hljs-title">easyspec</span><span class="hljs-params">(x,fs)</span></span>
<span class="hljs-comment">%EASYSPEC Easy plot of spectrum estimate</span>
<span class="hljs-comment">%         S=EASYSPEC(X) will return a spectrum vector of</span>
<span class="hljs-comment">%         X. [S,F]=EASYSPEC(X,FS) will also return a freuqency</span>
<span class="hljs-comment">%         axis in F, while the sample frequency is given in FS.</span>
<span class="hljs-comment">%         EASYSPEC(X) and EASYSPEC(X,FS), will plot the spectrum</span>
<span class="hljs-comment">%         and return the S vector in MATLAB's "ans".</span>
<span class="hljs-comment">%         Notes:</span>
<span class="hljs-comment">%         1. Spectrum values are in dB.</span>
<span class="hljs-comment">%         2. The frequency domain goes from DC to the Nyquist </span>
<span class="hljs-comment">%            frequency. The "mirror part" of the spectrum is</span>
<span class="hljs-comment">%            omitted.</span>
<span class="hljs-comment">%         3. The sample segments, from which the spectrum is</span>
<span class="hljs-comment">%            calculated are picked by random. The function might</span>
<span class="hljs-comment">%            return significantly different results each time it's</span>
<span class="hljs-comment">%            called to if X isn't stationary.</span>
<span class="hljs-comment">%         4. EASYSPEC uses a hanning window and zero-pads by a</span>
<span class="hljs-comment">%            factor of 4. The spectrum vector will look smooth.</span>

<span class="hljs-comment">% Eli Billauer, 17.1.01</span>
<span class="hljs-comment">% This function is released to the public domain; Any use is allowed.</span>

<span class="hljs-keyword">if</span> nargin==<span class="hljs-number">0</span>
  error(<span class="hljs-string">'No input vector given'</span>);
<span class="hljs-keyword">end</span>

<span class="hljs-keyword">if</span> (nargin==<span class="hljs-number">1</span>)
  fs=<span class="hljs-number">2</span>;
<span class="hljs-keyword">end</span>

NFFT=<span class="hljs-number">16384</span>; NWIN=NFFT/<span class="hljs-number">4</span>;
LOOP=<span class="hljs-number">100</span>;
win=hanning(NWIN)';

x=x(:)'*(<span class="hljs-number">17.127</span>/NFFT/<span class="hljs-built_in">sqrt</span>(LOOP));
n=<span class="hljs-built_in">length</span>(x);
maxshift=n-NWIN;

<span class="hljs-keyword">if</span> (n&lt;<span class="hljs-number">2</span>*NWIN)
  error([<span class="hljs-string">'Input vector should be at least of length '</span>...
      num2str(<span class="hljs-number">2</span>*NWIN)]);
<span class="hljs-keyword">end</span>

s=<span class="hljs-built_in">zeros</span>(<span class="hljs-number">1</span>,NFFT);

<span class="hljs-keyword">for</span> <span class="hljs-built_in">i</span>=<span class="hljs-number">1</span>:LOOP
  zuz=<span class="hljs-built_in">floor</span>(<span class="hljs-built_in">rand</span>*maxshift);
  s=s+<span class="hljs-built_in">abs</span>(fft([win.*x(<span class="hljs-number">1</span>+zuz:NWIN+zuz) <span class="hljs-built_in">zeros</span>(<span class="hljs-number">1</span>,NFFT-NWIN)])).^<span class="hljs-number">2</span>;
<span class="hljs-keyword">end</span>

s=<span class="hljs-number">10</span>*<span class="hljs-built_in">log10</span>(s(<span class="hljs-number">1</span>:NFFT/<span class="hljs-number">2</span>));
f=<span class="hljs-built_in">linspace</span>(<span class="hljs-number">0</span>,fs/<span class="hljs-number">2</span>,NFFT/<span class="hljs-number">2</span>);

<span class="hljs-keyword">if</span> nargout==<span class="hljs-number">0</span>
  <span class="hljs-built_in">hold</span> off;
  <span class="hljs-built_in">plot</span>(f,s);
  ylabel(<span class="hljs-string">'Power Spectrum [dB]'</span>);
  xlabel(<span class="hljs-string">'Frequency'</span>);
	grid on; zoom on;
<span class="hljs-keyword">end</span></pre>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2009/01/easyspec-matlab-octave-spectrum-analyzer/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
