<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://yedhu.me/feed.xml" rel="self" type="application/atom+xml" /><link href="https://yedhu.me/" rel="alternate" type="text/html" hreflang="en" /><updated>2026-10-10T06:46:10+05:30</updated><id>https://yedhu.me/feed.xml</id><title type="html">Yedhu Krishnan</title><subtitle>Notes on programming, books, and things I&apos;m learning.</subtitle><author><name>Yedhu Krishnan</name><email>yk.strobe281@passmail.com</email></author><entry><title type="html">Only Time Will Tell</title><link href="https://yedhu.me/posts/only-time-will-tell/" rel="alternate" type="text/html" title="Only Time Will Tell" /><published>2026-10-08T07:00:00+05:30</published><updated>2026-10-08T07:00:00+05:30</updated><id>https://yedhu.me/posts/only-time-will-tell</id><content type="html" xml:base="https://yedhu.me/posts/only-time-will-tell/"><![CDATA[<p>Jeffrey Archer passed away this week. I don’t know much about his personal or political life; I want to write about my experience with his books.</p>

<p>A bit of my background: I started reading books at a very young age. Back then, I mostly read in my mother tongue, Malayalam. My English comprehension wasn’t great in school. But I read books like Harry Potter, even though I understood only about 10% of it at the time. I later read Harry Potter and the Philosopher’s Stone in Malayalam and realized how much I hadn’t understood.</p>

<p>My first encounter with Jeffrey Archer was through my father. He heard about one of his books from someone. I don’t know exactly how the conversation went, but someone who was reading it mentioned that the book was much better than Harry Potter.</p>

<p>That comparison doesn’t make sense when I think about it now; the book was A Prison Diary, this is Archer’s non-fiction account of his life in prison, while Harry Potter is a work of fiction.</p>

<p>Anyway, my father knew how much I loved reading. He also knew that I read Harry Potter. Although he didn’t know anything about Jeffrey Archer or English books, he went to a bookstore and bought the book for me. That was so nice of him.</p>

<p>But sadly, the book was beyond my reading level at the time. I was already struggling with Harry Potter; I couldn’t keep up with the pace and had to put it down.</p>

<p>As the years passed, I picked up more English books and started understanding better. I also started having more real-life conversations in English, at my college and later at work.</p>

<p>Almost a decade passed before I picked up another book by Jeffrey Archer. I started reading Only Time Will Tell, the story of Harry Clifton, his life as a young kid, and his struggles with school and the people around him.</p>

<p>I finished the book and loved it. The book was an easy read, although some characters were complicated. I didn’t stop with the first book. I read all seven books in the series twice.</p>

<p>This was the second series I finished, and it had seven books, and the protagonist’s name is Harry. Combine that with what the guy said about the other book being better than Harry Potter, and I feel that is a funny coincidence.</p>

<p>Why did I love the series? I loved the characters, not just Harry Clifton. The story has good and bad people, and the entire series felt like watching a Movie. Here is something I wrote in my original <a href="/posts/only-time-will-tell-review/">review</a>:</p>

<blockquote>
  <p>I loved the characters, especially Maisie Clifton, Captain Jack Tarrant (Old Jack), and Emma Barrington; they prove how passionate and true a love can be as a mother, a mentor, and a lover, respectively. I loved Harry and Emma as a couple.</p>
</blockquote>

<p>Harry Potter is one of my favorites, and I have read the entire series at least four times; much more than Clifton Chronicles. Over the years, I’ve realized that if someone says one book is better than another, that point is completely subjective.</p>

<p>I am older now and have more experience reading fiction and non-fiction. It feels like the right time to pick up A Prison Diary and see it from that reader’s perspective to understand why he thought it was better than Harry Potter.</p>]]></content><author><name>Yedhu Krishnan</name><email>yk.strobe281@passmail.com</email></author><category term="Journal" /><category term="thoughts" /><category term="reading" /><category term="journal" /><summary type="html"><![CDATA[Jeffrey Archer passed away this week. I don’t know much about his personal or political life; I want to write about my experience with his books.]]></summary></entry><entry><title type="html">Commit Description as a Thinking Tool</title><link href="https://yedhu.me/posts/commit-description-as-a-thinking-tool/" rel="alternate" type="text/html" title="Commit Description as a Thinking Tool" /><published>2026-09-30T22:00:00+05:30</published><updated>2026-09-30T22:00:00+05:30</updated><id>https://yedhu.me/posts/commit-description-as-a-thinking-tool</id><content type="html" xml:base="https://yedhu.me/posts/commit-description-as-a-thinking-tool/"><![CDATA[<p>Before the AI era, I wrote pretty long commit descriptions (or commit bodies) for major changes. It took me about five to ten minutes to draft and reread them to make sure I didn’t miss anything important. I did that for several reasons.</p>

<p>I wanted to include all the useful information so readers wouldn’t have to hunt for it in multiple places.</p>

<p>I wanted to explain what and, more importantly, why. The “what” summarizes the changes that are generally self-explanatory, but it gives a starting point for explaining the “why.”</p>

<p>Sometimes, I write it in first person, like I am drafting a message for someone: “I did this because…”, “I am doing this until we…” etc. I then go ahead and explain why, so that it is easier for others, and most importantly for my future self, to understand why we made that change.</p>

<p>It was a good exercise. It wasn’t just about writing the commit message and description. The writing process itself helps me reflect on the code I wrote. I reread the code and summarize the changes. During that process, I tend to re-evaluate the decisions, and sometimes that leads to a different or better change.</p>

<p>Now we are in the era of agentic coding, where everything from code to commit descriptions is written by AI.  There is a huge debate on whether we should read the AI-written code, and how difficult that is in terms of readability. The part I find difficult is reading and understanding AI-written commit descriptions.</p>

<p>Agents can write commit messages for the changes they make. But they may not have the full context that is spread across different communication and project management tools. Some of those might be offline too. When the AI doesn’t know the ‘why’ part, it comes up with its own reasoning. I find that dangerous. When we read that later, it may not make sense, because the real reason was completely different.</p>

<p>One obvious solution is to give the agent all the context it needs, through chat or tools. This helps to fix the issues with fabricated reasoning. The agent can now explain the “why” clearly.</p>

<p>But this doesn’t fix the other problem. The agent with the right context will write a convincing commit message. But only I can verify if the code does what the description says. So, here is something I do:</p>

<p>Write the commit message and description myself.</p>

<p>Here is why.</p>

<p>Writing a commit description myself helps me to reflect on the changes AI made. It is also a way to check if everything is as intended. If I cannot explain “why,” I am shipping something I don’t understand, which will be hard to explain or fix if it breaks later. It goes back to the old quote. If you cannot explain it, you did not understand it. The commit description again works here as a thinking tool.</p>

<p>Some parts, like “I am doing this until we…”, are temporary decisions with exit criteria. We sometimes set exit conditions, and AI cannot infer them from code or other tools because they are usually not written down anywhere since they seem too obvious to mention. But writing the commit message forces me to complete that sentence, and it helps future readers decide whether to keep that change.</p>

<p>The agent can write the code and the description. But writing why is where you find out whether you understand what you are shipping.</p>]]></content><author><name>Yedhu Krishnan</name><email>yk.strobe281@passmail.com</email></author><category term="Software Development" /><category term="thoughts" /><category term="agentic-coding" /><summary type="html"><![CDATA[Before the AI era, I wrote pretty long commit descriptions (or commit bodies) for major changes. It took me about five to ten minutes to draft and reread them to make sure I didn’t miss anything important. I did that for several reasons.]]></summary></entry><entry><title type="html">Time Tracker App for macOS</title><link href="https://yedhu.me/posts/time-tracker/" rel="alternate" type="text/html" title="Time Tracker App for macOS" /><published>2026-07-12T05:30:00+05:30</published><updated>2026-07-12T05:30:00+05:30</updated><id>https://yedhu.me/posts/time-tracker</id><content type="html" xml:base="https://yedhu.me/posts/time-tracker/"><![CDATA[<p>It has been more than 15 years since I tried building a native desktop app. The last one was during my school days, when I built a calculator in Visual C++. That was fun and exciting, and my skills were limited. I knew the basics of C, and I drag-and-dropped different buttons and boxes to make the UI. I enjoyed building that app, although I was mostly the only one who saw it.</p>

<p>Fast forward 15+ years, and I spent some time building another native app. This time, it was a macOS time tracker. In terms of skill set, I am starting from scratch this time since I have no experience building a macOS app. But developing an app is easier with AI tools if you know what you want. I was able to vibecode a working version in two days, tailored for my needs.</p>

<p>In addition to normal time tracker features like the ability to start, pause, and stop the timer and set an agenda, I wanted a few other features as well:</p>

<ol>
  <li>A customizable reminder (nudge) to track the time during work hours. I could also customize the work hours in the app.</li>
  <li>A configurable nudge interval and a snooze option.</li>
  <li>A prompt at the end of the session to reflect on how well it went. I could write a short summary of the session if I need to.</li>
  <li>An option to rate the session from 1 to 5.</li>
  <li>Sync across my MacBook and Mac Mini since I switch between them often.</li>
</ol>

<p>I’m sure there are apps with some or most of these features, but the prospect of building something by myself was tempting. Also, it gives me full control over the app’s features and appearance.</p>

<p><img src="/assets/img/2026/time-tracker-ui.png" alt="Time Tracker" width="75%" /></p>

<p class="image-caption"><em>The time tracker UI</em></p>

<p>This was the first app I built for the Apple ecosystem. For anyone curious, the source code and the disk images are available here: <a href="https://github.com/yedhukrishnan/time-tracker" target="_blank">time-tracker</a></p>]]></content><author><name>Yedhu Krishnan</name><email>yk.strobe281@passmail.com</email></author><category term="Software Development" /><category term="apps" /><category term="macos" /><category term="productivity" /><summary type="html"><![CDATA[It has been more than 15 years since I tried building a native desktop app. The last one was during my school days, when I built a calculator in Visual C++. That was fun and exciting, and my skills were limited. I knew the basics of C, and I drag-and-dropped different buttons and boxes to make the UI. I enjoyed building that app, although I was mostly the only one who saw it.]]></summary></entry><entry><title type="html">Image Manipulation in Python</title><link href="https://yedhu.me/posts/image-manipulation-in-python/" rel="alternate" type="text/html" title="Image Manipulation in Python" /><published>2022-10-28T05:30:00+05:30</published><updated>2022-10-28T05:30:00+05:30</updated><id>https://yedhu.me/posts/image-manipulation-in-python</id><content type="html" xml:base="https://yedhu.me/posts/image-manipulation-in-python/"><![CDATA[<p>If you haven’t read the last post in the series, please read it here: <a href="/posts/the-r-g-and-b-in-an-image/">The R, G, and B in an Image</a>.</p>

<p>Let’s see how we increase/decrease the brightness or contrast of an image and some other transformations. We already know that images are NumPy arrays in Python. All we need to do now is to perform matrix operations.</p>

<p>Let’s start by reading a greyscale image.</p>

<p><img src="/assets/img/2022/grayscale-image.webp" alt="A gray tree" />
<em>Image Courtesy: pexels.com</em></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">import</span> <span class="n">imageio</span>
<span class="n">image</span> <span class="o">=</span> <span class="n">imageio</span><span class="p">.</span><span class="nf">imread</span><span class="p">(</span><span class="sh">'</span><span class="s">grey_tree.jpg</span><span class="sh">'</span><span class="p">)</span>
</code></pre></div></div>

<p>Let’s say we would like to increase the brightness of the image. To do that, add a constant value to each pixel. This is how we do that in NumPy</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">bright_image</span> <span class="o">=</span> <span class="n">image</span> <span class="o">+</span> <span class="mi">50</span>
<span class="n">bright_image</span><span class="p">[</span><span class="n">bright_image</span> <span class="o">&gt;</span> <span class="mi">255</span><span class="p">]</span> <span class="o">=</span> <span class="mi">255</span>
<span class="n">imageio</span><span class="p">.</span><span class="nf">imwrite</span><span class="p">(</span><span class="sh">"</span><span class="s">bright_grey_tree.jpg</span><span class="sh">"</span><span class="p">,</span> <span class="n">bright_image</span><span class="p">.</span><span class="nf">astype</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">uint8</span><span class="p">))</span>
</code></pre></div></div>

<p>In the first line, I added 50 to each pixel value. When we do that, some pixels will go above the 8-bit image threshold of 255. When that happens, we set it back to 255, the highest possible value. Then I saved the matrix as an 8-bit image.</p>

<p>As you see in the snippet above, NumPy makes it easier to change all the pixel values without iterating over them in a loop. That is the beauty of the library.</p>

<p>If we save the image, we will see a brighter image like this:</p>

<p><img src="/assets/img/2022/bright-image.webp" alt="A bright gray tree" /></p>

<p>You can guess what we would do to decrease the brightness now. We need to subtract a constant value from each pixel. If the number goes negative, set it to 0.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dark_image</span> <span class="o">=</span> <span class="n">image</span> <span class="o">-</span> <span class="mi">50</span>
<span class="n">dark_image</span><span class="p">[</span><span class="n">dark_image</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">imageio</span><span class="p">.</span><span class="nf">imwrite</span><span class="p">(</span><span class="sh">"</span><span class="s">dark_grey_tree.jpg</span><span class="sh">"</span><span class="p">,</span> <span class="n">dark_image</span><span class="p">.</span><span class="nf">astype</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">uint8</span><span class="p">))</span>
</code></pre></div></div>

<p>And the resulting image looks like this:</p>

<p><img src="/assets/img/2022/dark-image.webp" alt="A dark gray tree" /></p>

<p>We can adjust the brightness in percentages too. Convert the percentage value to the pixel value using the formula:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pixel_value = 255 * (percentage_value / 100)
</code></pre></div></div>

<p>To adjust the contrast of an image, instead of adding or removing a constant, we multiple each pixel with a number:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">high_contrast_image</span> <span class="o">=</span> <span class="n">image</span> <span class="o">*</span> <span class="mf">1.5</span>
<span class="n">high_contrast_image</span><span class="p">[</span><span class="n">high_contrast_image</span> <span class="o">&gt;</span> <span class="mi">255</span><span class="p">]</span> <span class="o">=</span> <span class="mi">255</span>
<span class="n">imageio</span><span class="p">.</span><span class="nf">imwrite</span><span class="p">(</span><span class="sh">"</span><span class="s">high_contrast_grey_tree.jpg</span><span class="sh">"</span><span class="p">,</span> <span class="n">high_contrast_image</span><span class="p">.</span><span class="nf">astype</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">uint8</span><span class="p">))</span>
<span class="n">low_contrast_image</span> <span class="o">=</span> <span class="n">image</span> <span class="o">*</span> <span class="mf">0.5</span>
<span class="n">low_contrast_image</span><span class="p">[</span><span class="n">low_contrast_image</span> <span class="o">&gt;</span> <span class="mi">255</span><span class="p">]</span> <span class="o">=</span> <span class="mi">255</span>
<span class="n">imageio</span><span class="p">.</span><span class="nf">imwrite</span><span class="p">(</span><span class="sh">"</span><span class="s">high_contrast_grey_tree.jpg</span><span class="sh">"</span><span class="p">,</span> <span class="n">low_contrast_image</span><span class="p">.</span><span class="nf">astype</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">uint8</span><span class="p">)</span>
</code></pre></div></div>

<p>Here are the corresponding versions of the image.</p>

<p><img src="/assets/img/2022/high-contrast-image.webp" alt="A high contrast gray tree" /> <img src="/assets/img/2022/low-contrast-image.webp" alt="A low contrast gray tree" /></p>

<p>As you can see, multiplying by a number greater than 1 increases the contrast, while multiplying by a number less than 1 decreases the contrast.</p>

<p>What if you want to flip the image? All you need to do is to reverse the rows in the matrix:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">flipped_image</span> <span class="o">=</span> <span class="n">image</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="p">:]</span>
<span class="n">imageio</span><span class="p">.</span><span class="nf">imwrite</span><span class="p">(</span><span class="sh">"</span><span class="s">flipped_image.jpg</span><span class="sh">"</span><span class="p">,</span> <span class="n">flipped_image</span><span class="p">.</span><span class="nf">astype</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">uint8</span><span class="p">)</span>
</code></pre></div></div>

<p><img src="/assets/img/2022/flipped-image.webp" alt="A flipped gray tree" /></p>

<p><code class="language-plaintext highlighter-rouge">image[::-1, :]</code> reverses the rows in the image <code class="language-plaintext highlighter-rouge">(::-1)</code> and keeps the columns intact (<code class="language-plaintext highlighter-rouge">:</code>). You can get the mirror image by doing the same for columns instead of rows.</p>

<p>I recommend going through the <a href="https://numpy.org/doc/1.23/user/absolute_beginners.html">NumPy docs</a> to learn more about how these operators work. Read an image, and play around with it. Search about various matrix operations, read how you can do that with NumPy, and try them on your image. See how the image transforms when you do that.</p>

<p>In future posts, I will explain how we can do color image manipulation. For now, please share your feedback and thoughts about these posts.</p>

<p>I’ve also published the article on Medium: <a href="https://medium.com/@yedhu/image-manipulation-in-python-98db79cc0c1">Image Manipulation in Python</a>.</p>]]></content><author><name>Yedhu Krishnan</name><email>yk.strobe281@passmail.com</email></author><category term="Digital Image Processing" /><category term="python" /><category term="digital-image-processing" /><summary type="html"><![CDATA[If you haven’t read the last post in the series, please read it here: The R, G, and B in an Image.]]></summary></entry><entry><title type="html">The R, G, and B in an Image</title><link href="https://yedhu.me/posts/the-r-g-and-b-in-an-image/" rel="alternate" type="text/html" title="The R, G, and B in an Image" /><published>2019-10-08T05:30:00+05:30</published><updated>2019-10-08T05:30:00+05:30</updated><id>https://yedhu.me/posts/the-r-g-and-b-in-an-image</id><content type="html" xml:base="https://yedhu.me/posts/the-r-g-and-b-in-an-image/"><![CDATA[<p>If you haven’t read the last post in the series, please read it here: <a href="/posts/introduction-to-digital-image-processing-in-python">Introduction to Digital Image Processing in Python</a>.</p>

<p>A typical color image consists of three color channels: red, green and blue. Each color channel has 8 bits and can represent 256 distinct values. Using a combination of all three channels, we can create <code class="language-plaintext highlighter-rouge">256 x 256 x 256</code> colors, which is around 16-million colors. You might have heard this term before. Now you know where that came from.</p>

<p>In this post, we will learn more about the different components of an image. Let us try everything in a python console, which can be started by running the following command in a terminal. If you haven’t installed Python yet, please follow the <a href="/posts/introduction-to-digital-image-processing-in-python">instructions from my previous post</a>.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python3
</code></pre></div></div>

<p>You can download and use the sample image given below. It has different colors, and that’s what we need.</p>

<p><img src="/assets/img/2019/colors.webp" alt="A colorful image with red, green, blue, yellow, orange and white straws" />
<em>Image Courtesy: Max Pixel</em></p>

<p>Import the library and load the image:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">imageio</span>
<span class="n">image</span> <span class="o">=</span> <span class="n">imageio</span><span class="p">.</span><span class="nf">imread</span><span class="p">(</span><span class="sh">'</span><span class="s">colors.jpg</span><span class="sh">'</span><span class="p">)</span>
</code></pre></div></div>

<p>Now we have the image as a NumPy array in our console. If you want to see the dimensions of the image, run:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">image</span><span class="p">.</span><span class="n">shape</span>
</code></pre></div></div>

<p>You will get something like <code class="language-plaintext highlighter-rouge">(426, 640, 3)</code>. This means the image has a height of 426 pixels, a width of 640 pixels, and three color channels. We are interested in the color channels for now. Let us write the individual channels to separate image files:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">imageio</span><span class="p">.</span><span class="nf">imwrite</span><span class="p">(</span><span class="sh">'</span><span class="s">red.jpg</span><span class="sh">'</span><span class="p">,</span> <span class="n">image</span><span class="p">[:,</span> <span class="p">:,</span> <span class="mi">0</span><span class="p">])</span>
<span class="n">imageio</span><span class="p">.</span><span class="nf">imwrite</span><span class="p">(</span><span class="sh">'</span><span class="s">green.jpg</span><span class="sh">'</span><span class="p">,</span> <span class="n">image</span><span class="p">[:,</span> <span class="p">:,</span> <span class="mi">1</span><span class="p">])</span>
<span class="n">imageio</span><span class="p">.</span><span class="nf">imwrite</span><span class="p">(</span><span class="sh">'</span><span class="s">blue.jpg</span><span class="sh">'</span><span class="p">,</span> <span class="n">image</span><span class="p">[:,</span> <span class="p">:,</span> <span class="mi">2</span><span class="p">])</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">:</code> is used to get all values. For example, <code class="language-plaintext highlighter-rouge">image[:, :, 0]</code> means get all rows, all columns, and the first (at index 0) color channel, which is the red channel. I’ll talk more about Indexing and Slicing in the next post. If you are curious, you can learn more about here: <a href="https://docs.scipy.org/doc/numpy-1.13.0/reference/arrays.indexing.html">Indexing and Slicing</a> (or <a href="https://www.tutorialspoint.com/numpy/numpy_indexing_and_slicing.htm">here</a>).</p>

<p>If you open the files you just created, you would see something like this.</p>

<p><img src="/assets/img/2019/r.webp" alt="r" /> <img src="/assets/img/2019/g.webp" alt="g" /> <img src="/assets/img/2019/b.webp" alt="b" />
<em>The red, blue and green components</em></p>

<p>Why do they all look grey?</p>

<p>That is because they are single-channel images now. They will be saved as greyscale images. If you see the shape of the green component using the command:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">image</span><span class="p">[:,</span> <span class="p">:,</span> <span class="mi">1</span><span class="p">].</span><span class="n">shape</span>
</code></pre></div></div>

<p>You will see <code class="language-plaintext highlighter-rouge">(426, 460)</code>. The color channel part is missing. Each pixel will have a single 8-bit value (unlike in RGB, which has three 8-bit values for R, G and B).</p>

<p>But if observe carefully, you will see that the grey value in the red-channel image corresponds to the amount of red in the color image.</p>

<p><img src="/assets/img/2019/red.webp" alt="Color distribution for red channel" /></p>

<p>The distribution of red color. On the extreme left, the value of red would be 0 and on the extreme right, it would be 255. As we move from left to write, the value gradually increases.</p>

<p>For example, see a red straw in the color image. A red pixel would be <code class="language-plaintext highlighter-rouge">[255, 0, 0]</code>, which is on the right side of the color distribution image shown above. The green and blue components will be zero.</p>

<p>In red.jpg image, the color of the same straw is white, which is 255. And the same straw appears black (0) in both green and blue component images because a red pixel doesn’t have blue or green components.</p>

<p><img src="/assets/img/2019/all-colors.webp" alt="All color channels" /></p>

<p>Color distribution for greyscale, red, green and blue channels. In all cases, the left side is 0 and the right side is 255.
If you want to see the actual colors instead of just grey images, you need to retain all three channels and set the values on the other channels to zero. For example, to get the red component, set the green and blue channel values to 0, as shown below:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">red_image</span> <span class="o">=</span> <span class="n">image</span><span class="p">.</span><span class="nf">copy</span><span class="p">()</span>
<span class="n">red_image</span><span class="p">[:,</span> <span class="p">:,</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">red_image</span><span class="p">[:,</span> <span class="p">:,</span> <span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">imageio</span><span class="p">.</span><span class="nf">imwrite</span><span class="p">(</span><span class="sh">'</span><span class="s">red_image.jpg</span><span class="sh">'</span><span class="p">,</span> <span class="n">red_image</span><span class="p">)</span>
</code></pre></div></div>

<p>Here, I made a copy of the image and set all the blue and green channel values to zero. This is another handy usage of <code class="language-plaintext highlighter-rouge">:</code> to set the same value to multiple pixels in a NumPy array.</p>

<p>Similarly, you can create the green and blue images. They will now look like this:</p>

<p><img src="/assets/img/2019/r-color.webp" alt="R color" /> <img src="/assets/img/2019/g-color.webp" alt="G color" /> <img src="/assets/img/2019/b-color.webp" alt="B color" />
<em>The colorful red, green and blue components</em></p>

<p>Notice that for yellow straws, both red and green components are active, but the blue component is missing (black in color). The pixel values would be closer to <code class="language-plaintext highlighter-rouge">[255, 255, 0]</code>.</p>

<p>White color in RGB image would be represented as <code class="language-plaintext highlighter-rouge">[255, 255, 255]</code>. Grey pixels in the RGB image will have the same value for all components. When we take the average of all three values, we get the same number. For example, a grey value <code class="language-plaintext highlighter-rouge">[57, 57, 57]</code> in RGB will have value 57 in a greyscale image. We discussed about converting an <a href="/posts/lessons-on-digital-image-processing-1">RGB image to greyscale in the first post</a>.</p>

<p>Now you know how RGB images are made; by using a combination of values on R, G, and B color channels. The same technique is used in all modern color displays, including mobile and TV screens. Here is a <a href="https://www.youtube.com/watch?v=3BJU2drrtCM">cool video by The Slow Mo Guys</a> explaining how that works on TVs.</p>

<p>In the coming posts, I’ll explain about processing a digital image using Python. For now, please share your feedback and suggestions.</p>

<p>Originally published on Medium: <a href="https://medium.com/analytics-vidhya/the-r-g-and-b-in-an-image-971b9aecfb7f" target="_blank" rel="noopener">The R, G, and B in an Image</a>.</p>]]></content><author><name>Yedhu Krishnan</name><email>yk.strobe281@passmail.com</email></author><category term="Digital Image Processing" /><category term="python" /><category term="digital-image-processing" /><summary type="html"><![CDATA[If you haven’t read the last post in the series, please read it here: Introduction to Digital Image Processing in Python.]]></summary></entry><entry><title type="html">Introduction to Digital Image Processing in Python</title><link href="https://yedhu.me/posts/introduction-to-digital-image-processing-in-python/" rel="alternate" type="text/html" title="Introduction to Digital Image Processing in Python" /><published>2019-09-29T05:30:00+05:30</published><updated>2019-09-29T05:30:00+05:30</updated><id>https://yedhu.me/posts/introduction-to-digital-image-processing-in-python</id><content type="html" xml:base="https://yedhu.me/posts/introduction-to-digital-image-processing-in-python/"><![CDATA[<p>If you haven’t read the first post in the series, read it here: <a href="/posts/lessons-on-digital-image-processing-1/">Lessons on Digital Image Processing</a>.</p>

<p>In this second part, let’s write some actual code. We will be writing all our code in Python.</p>

<p>Python makes processing and manipulating images very easy. NumPy and SciPy packages available for Python help us to perform scientific computing, mainly operations on matrices, which is of interest to us. We already saw images are just matrices in computers.</p>

<p>The easiest way to install SciPy and NumPy in a Linux machine is using Python package manager pip.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt-get <span class="nb">install </span>python3-pip
<span class="nb">sudo </span>pip3 <span class="nb">install </span>numpy scipy
</code></pre></div></div>

<p>Let’s install two more packages which are necessary to load and display an image in Python.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>pip3 <span class="nb">install </span>imageio matplotlib
</code></pre></div></div>

<p>Now, to load and display an image, all you need is a few lines of code. Make sure you have an image file in the same directory where your code is residing.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">imageio</span>
<span class="kn">import</span> <span class="n">matplotlib.pyplot</span> <span class="k">as</span> <span class="n">plt</span>

<span class="c1"># Load the image
</span><span class="n">image</span> <span class="o">=</span> <span class="n">imageio</span><span class="p">.</span><span class="nf">imread</span><span class="p">(</span><span class="sh">'</span><span class="s">color_flower.jpg</span><span class="sh">'</span><span class="p">)</span>

<span class="c1"># Display the image
</span><span class="n">plt</span><span class="p">.</span><span class="nf">imshow</span><span class="p">(</span><span class="n">image</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="nf">show</span><span class="p">()</span>
</code></pre></div></div>

<p>That’s all. The code is self-explanatory. I have added necessary comments to help you understand.</p>

<p>We have a color image. How do we create a greyscale image from it?</p>

<p>We already know a color image has three values in each pixel positions for representing red, green and blue intensity values. In a greyscale image, there is only one value. All we need to do is to convert these three values into a single value.
How do we do that?</p>

<p>Just take the average of all the three values!</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>grey pixel value = (red + green + blue) / 3
</code></pre></div></div>

<p>Let us implement this in Python. We use a NumPy array to store the image in Python. NumPy provides a convenient function to take the average or mean on any axis. In the case of an RGB image, axis = 0 is row-wise, axis = 1 is column-wise and axis = 2 is channel-wise. We need a channel-wise sum.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gray_image = image.mean(axis = 2)
</code></pre></div></div>

<p>This one line of code converts the color image to a grayscale image. It does the same thing as the following lines of code:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="n">row</span><span class="p">,</span> <span class="n">col</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="o">=</span> <span class="n">image</span><span class="p">.</span><span class="n">shape</span>

<span class="c1"># Define an empty array for grey image
</span><span class="n">grey_image</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nf">zeros</span><span class="p">((</span><span class="n">row</span><span class="p">,</span> <span class="n">col</span><span class="p">))</span>

<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">row</span><span class="p">):</span>
    <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">col</span><span class="p">):</span>
        <span class="c1"># Store the average of R, G, B values
</span>        <span class="n">grey_image</span><span class="p">[</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">]</span> <span class="o">=</span> <span class="n">image</span><span class="p">[</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">].</span><span class="nf">mean</span><span class="p">()</span>

<span class="n">plt</span><span class="p">.</span><span class="nf">imshow</span><span class="p">(</span><span class="n">grey_image</span><span class="p">,</span> <span class="n">cmap</span><span class="o">=</span><span class="sh">'</span><span class="s">gray</span><span class="sh">'</span><span class="p">);</span> <span class="n">plt</span><span class="p">.</span><span class="nf">show</span><span class="p">()</span>
</code></pre></div></div>

<p>The above code is much slower than the previous version. However, understanding this is important if you are new to this.</p>

<p>We just created the grey version of the image. Now, we can convert this into a binary image. A grey image pixel values range from 0 to 255. In binary, it can take only two values. In some representations, we use 0 and 1. Here, we’ll use 0 (for black) and 255 (for white).</p>

<p>How do we convert a grey image into a binary image? The usual method is to set a threshold value T. If the pixel value is above the threshold, we’ll set it to 255. If it is below, we’ll set it to 0. Here, we can take the threshold as the median grey value, which is 128.</p>

<p>Here is the extended version of the code.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">binary_image</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nf">zeros</span><span class="p">((</span><span class="n">row</span><span class="p">,</span> <span class="n">col</span><span class="p">))</span>

<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">row</span><span class="p">):</span>
    <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">col</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">grey_image</span><span class="p">[</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="mi">128</span><span class="p">:</span>
            <span class="n">pixel</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">pixel</span> <span class="o">=</span> <span class="mi">255</span>

        <span class="n">binary_image</span><span class="p">[</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">]</span> <span class="o">=</span> <span class="n">pixel</span>

<span class="n">plt</span><span class="p">.</span><span class="nf">imshow</span><span class="p">(</span><span class="n">binary_image</span><span class="p">,</span> <span class="n">cmap</span><span class="o">=</span><span class="sh">'</span><span class="s">gray</span><span class="sh">'</span><span class="p">);</span> <span class="n">plt</span><span class="p">.</span><span class="nf">show</span><span class="p">()</span>
</code></pre></div></div>

<p>And the short version is just two lines:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>binary_image = grey_image.copy()
binary_image[binary_image &lt;= 128] = 0
binary_image[binary_image &gt; 128]  = 255
</code></pre></div></div>

<p>That is it! NumPy takes care of updating all the pixel values to 0 or 255 depending on whether they are above or below the threshold.</p>

<p>Now we know about color, greyscale and binary images, and we know how to convert a color image to grey and then to binary. We will see more image processing techniques and python code in the upcoming posts.</p>

<p>For now, please share your feedback and opinion about these posts.</p>

<p>Originally published on Medium: <a href="https://medium.com/analytics-vidhya/lessons-on-digital-image-processing-2-983d8bab98c8">Introduction to Digital Image Processing in Python</a>.</p>]]></content><author><name>Yedhu Krishnan</name><email>yk.strobe281@passmail.com</email></author><category term="Digital Image Processing" /><category term="python" /><category term="digital-image-processing" /><summary type="html"><![CDATA[If you haven’t read the first post in the series, read it here: Lessons on Digital Image Processing.]]></summary></entry><entry><title type="html">Book Review: Malice</title><link href="https://yedhu.me/posts/malice/" rel="alternate" type="text/html" title="Book Review: Malice" /><published>2018-12-28T11:07:00+05:30</published><updated>2018-12-28T11:07:00+05:30</updated><id>https://yedhu.me/posts/malice</id><content type="html" xml:base="https://yedhu.me/posts/malice/"><![CDATA[<blockquote>
  <p><em>Everyone has secrets. And everyone has the right to keep them. Even if they’re dead.</em></p>
</blockquote>

<p>Keigo Higashino never fails to amuse me with perfect crimes and gripping mystery behind it. As usual, by the end of first chapter, the murder was committed, the how part was almost clear and we had a prime suspect. The rest of the story is a quest to find the motive behind the murder. Just when I thought it was over, the book had over 40% remaining and it was full of twists and turns.</p>

<p>Kunihiko Hidaka, a famous Japanese author, is found murdered in his house days before he was supposed to move to Canada. The body was discovered by his wife and his best friend. While investigating, Detective Kyoichiro Kaga comes across information that questions the authenticity of the author and the relationship with his friend.</p>

<p>The story resembles Devotion of Suspect X in many ways. There are old colleagues who know each other well. The conversation between them leads to a turning point in the investigation. But while trying to uncover the plot, the detective has to dig up far more than the suspect’s past known to him.</p>

<p>Unlike other stories, Malice is written from the point of view of the detective and the victim’s friend. This helps in setting the plot at the beginning. It also helped to break some conventional assumptions I had about the story.</p>

<blockquote>
  <p><em>Apparently, it won’t do just to tell the reader what a particular character is like. The author needs to show their habits or their words and let the reader form an image on their own.</em></p>
</blockquote>

<p>I place Malice at the third position of my list of favourite Higashino novels. It comes after Devotion of Suspect X and Salvation of a Saint, pushing Journey Under the Midnight Sun and Midsummer’s Equation one step backward. But I recommend this to anyone who loves crime thrillers.</p>

<p>Originally published on my blog: <a href="https://mirrordimension.wordpress.com/2018/12/28/malice/" target="_blank" rel="noopener">Malice</a></p>]]></content><author><name>Yedhu Krishnan</name><email>yk.strobe281@passmail.com</email></author><category term="Book Review" /><category term="books" /><category term="fiction" /><summary type="html"><![CDATA[Everyone has secrets. And everyone has the right to keep them. Even if they’re dead.]]></summary></entry><entry><title type="html">Book Review: Factfulness</title><link href="https://yedhu.me/posts/factfulness/" rel="alternate" type="text/html" title="Book Review: Factfulness" /><published>2018-11-19T05:30:00+05:30</published><updated>2018-11-19T05:30:00+05:30</updated><id>https://yedhu.me/posts/factfulness</id><content type="html" xml:base="https://yedhu.me/posts/factfulness/"><![CDATA[<blockquote>
  <p>Step-by-step, year-by-year, the world is improving. Not on every single measure every single year, but as a rule. Though the world faces huge challenges, we have made tremendous progress. This is the fact-based worldview.</p>
</blockquote>

<p>Factfulness is one book I would recommend to anyone who wants to have a clear perspective about the world based on facts. It was written by Hans Rosling; a medical doctor, professor of international health and a public educator. I recently found this TED video; and interesting one which will help you know him better: The best stats you’ve ever seen. The book is a compilation of his years of research and experience, and is co-written by his son Ola Rosling and daughter-in-law Anna Rosling. I heard about it first when Bill Gates mentioned this book in his blog. He explains how it helped him not to talk about ‘developed’ and ‘developing’ countries.</p>

<p><img src="/assets/img/2018/factfulness-cover.jpeg" alt="Factfulness book cover" /></p>

<p>We live with misconceptions about the current state of the world. We hear the bad side of everything everyday and we think the world is slowly deteriorating. This over-dramatic view is what Hans Rosling demystifies in Factfulness. He presents why and how we are wrong through a simple quiz and explanations. It doesn’t end there. He further dives into what causes these distorted views, how to look out for the warning signs, and how to avoid them.</p>

<p>He describes ten instincts, in ten chapters, which are responsible for this. Although these one line explanations is nothing compared to the chapters in the book, if you do not like spoilers, you can skip this part.</p>

<ul>
  <li><strong>The gap instinct</strong>: the natural tendency to divide everything incorrectly into two groups</li>
  <li><strong>The negativity instinct</strong>: which always makes us see the negative face than the positive one</li>
  <li><strong>The straight line instinct</strong>: the assumption that a straight line will always continue like the same in the future when we look at graphs</li>
  <li><strong>The fear instinct</strong>: frightening things that get in our way and blocks rational thinking</li>
  <li><strong>The size instinct</strong>: when lonely numbers are presented to us, we wrongly assume the severity of the issue</li>
  <li><strong>The generalisation instinct</strong>: which makes us think something that works for someone, works for others too</li>
  <li><strong>The destiny instinct</strong>: when people think something will never change because they don’t observe gradual progress</li>
  <li><strong>The single perspective instinct</strong>: the problem of a single perspective limiting our imagination</li>
  <li><strong>The blame instinct</strong>: when we find comfort in pointing fingers rather than finding the root cause</li>
  <li><strong>The urgency instinct</strong>: which makes us act fast without thinking, by creating a sense of unnecessary urgency</li>
</ul>

<p>He presents every one of these with case studies and beautiful bubble charts. All the facts are backed by data, which are compiled and verified by international organisations. Also, the author is not afraid to admit how he had these instincts in the past, some of which are quintessential and resulted in terrifying repercussions.</p>

<p>The chapters are clear, concise and well structured with explanations and data. There was never a dull moment reading this book, and I enjoyed it all of it! I can count the non-fictions I loved from cover to cover on the fingers (The last one was Delivering Happiness, by late <em>Tony Hsieh</em>, founder of Zappos)</p>

<p><img src="/assets/img/2018/factfulness.jpg" alt="Factfulness" />
<em>“The world cannot be understood without numbers. But the world cannot be understood with numbers alone.”  – Hans Rosling</em></p>

<p>This book was an eye-opener for me. Also, this is not about optimism. The author himself says he is not an optimist, but a ‘possibiist’; someone who neither hopes without reason, nor fears without reason, someone who constantly resists the overdramatic worldview. It doesn’t say the world is good. It says the world is both good and bad; they coexist together. The learnings from the book can be applied in about everything in our daily life.</p>

<p>Originally published on my blog: <a href="https://mirrordimension.wordpress.com/2018/11/29/factfulness/" target="_blank" rel="noopener">Factfulness</a></p>]]></content><author><name>Yedhu Krishnan</name><email>yk.strobe281@passmail.com</email></author><category term="Book Review" /><category term="books" /><category term="non-fiction" /><summary type="html"><![CDATA[Step-by-step, year-by-year, the world is improving. Not on every single measure every single year, but as a rule. Though the world faces huge challenges, we have made tremendous progress. This is the fact-based worldview.]]></summary></entry><entry><title type="html">Book Review: Journey Under the Midnight Sun</title><link href="https://yedhu.me/posts/journey-under-the-midnight-sun/" rel="alternate" type="text/html" title="Book Review: Journey Under the Midnight Sun" /><published>2018-10-29T17:19:00+05:30</published><updated>2018-10-29T17:19:00+05:30</updated><id>https://yedhu.me/posts/journey-under-the-midnight-sun</id><content type="html" xml:base="https://yedhu.me/posts/journey-under-the-midnight-sun/"><![CDATA[<blockquote>
  <p>You know how the sun rises and sets at a certain time each day? In the same way, all of our lives have a day and night. But it’s not set like it is with the sun. Some people walk forever in the sunlight, and some people have to walk through the darkest night their whole lives. When people talk about being afraid, what they’re afraid of is that their sun will set. That the light they love will fade.</p>
</blockquote>

<p>Keigo Higashino is an acclaimed mystery novelist from Japan, and one of my favourite authors. He started writing novels in the 80’s and was popular in Japan for quite some time. But the world came to know about him when his crime thriller, <em>Yōgisha X no Kenshin</em>, was translated to English as <em>Devotion of Suspect X</em>. He writes perfect crimes which are, indeed, page-turners.</p>

<p><em>Journey Under the Midnight Sun</em> takes us through a time span of two decades. A murder is committed, and the specifics of it remains unresolved. Time goes by, life of everyone associated changes, but the tenacity of Sagasaki, the investigation officer, doesn’t. He pays attention to the ramifications of the murder all these years, and continues his search for answers even after his retirement, which leads to interesting discovery about the past.</p>

<p>The intricate structure of the plot is purely mesmerising and shows the sheer brilliance of the storyteller. You come across these unanticipated subplots, with new characters and events, that just pops up and connect with the main storyline afterward. The ending of the book is kind of abrupt, but gives a logical closure to the crusade.</p>

<p><em>“When you wander in the dark too long, you start to see things that aren’t really there.” — Keigo Higashino</em></p>

<p>If you haven’t read any books by Keigo Higashino, I would recommend reading Devotion of Suspect X first. I had trouble remembering all these Japanese character names. But that one didn’t have many characters and it helped me to focus on the story instead of going back and forth to see who is who. This wouldn’t be a problem if you have read Japanese novels before.</p>

<p>Originally published on my blog: <a href="https://mirrordimension.wordpress.com/2018/10/29/journey-under-the-midnight-sun/" target="_blank" rel="noopener">Journey Under the Midnight Sun</a></p>]]></content><author><name>Yedhu Krishnan</name><email>yk.strobe281@passmail.com</email></author><category term="Book Review" /><category term="books" /><category term="fiction" /><summary type="html"><![CDATA[You know how the sun rises and sets at a certain time each day? In the same way, all of our lives have a day and night. But it’s not set like it is with the sun. Some people walk forever in the sunlight, and some people have to walk through the darkest night their whole lives. When people talk about being afraid, what they’re afraid of is that their sun will set. That the light they love will fade.]]></summary></entry><entry><title type="html">Book Review: Creative Struggle</title><link href="https://yedhu.me/posts/zen-pencils-creative-struggle/" rel="alternate" type="text/html" title="Book Review: Creative Struggle" /><published>2018-10-16T05:30:00+05:30</published><updated>2018-10-16T05:30:00+05:30</updated><id>https://yedhu.me/posts/zen-pencils-creative-struggle</id><content type="html" xml:base="https://yedhu.me/posts/zen-pencils-creative-struggle/"><![CDATA[<blockquote>
  <p>And that’s what separates the pros from the hobbyists, the ability to create when you don’t feel like creating. The ability to “master their disinclination”</p>
</blockquote>

<p><img src="/assets/img/2018/curie.webp" alt="Marie Curie" /></p>

<p>Zen Pencils always lift my spirits. They are good for a quick shot of inspiration. The latest book is no different. Like Zen Pencils: Cartoon Quotes from Inspirational Folks and Dream the Impossible Dream, Creative Struggle is yet another collection of inspirational comics, most of which are available to read for free in <a href="https://www.zenpencils.com/" target="_blank">zenpencils.com</a>. I love to keep hardcopy versions because they are worth it, and are good for gifting.</p>

<p><img src="/assets/img/2018/creative-struggle-cover.jpg" alt="Factfulness book cover" /></p>

<p>Gavin Aung Than gives colours to these magnificent and inspirational quotes by famous personalities and transform them into stories. He illustrates the struggles they went through, how they conquered the fears and created their best pieces of work. He also gives a glimpse of the unexplored sides of these famous personalities we know.</p>

<p>Originally published on my blog: <a href="https://mirrordimension.wordpress.com/2018/10/16/zen-pencils-creative-struggle-illustrated-advice-from-masters-of-creativity/" target="_blank" rel="noopener">Zen Pencils — Creative Struggle: Illustrated Advice from Masters of Creativity</a></p>]]></content><author><name>Yedhu Krishnan</name><email>yk.strobe281@passmail.com</email></author><category term="Book Review" /><category term="books" /><category term="comics" /><summary type="html"><![CDATA[And that’s what separates the pros from the hobbyists, the ability to create when you don’t feel like creating. The ability to “master their disinclination”]]></summary></entry></feed>