Image processing with Go

A few days ago I wanted to do some batch image processing. It could be done in Photoshop, I know, but I wanted to have some fun as well and learn some algorithms. I started studying Go in January and it sounded like an opportunity to practice a little, so I began to write my own program to process images: blzimg.

blzimg will have some image operations. The first of them (and the only one until now) is called “lightest”. It merges the lightest pixels of a list of images into a single image.

Comparing the luminance of pixels

The first image operation I wanted to do was to get some images and, for every pixel (x,y) of them, their RGB values would be compared. The lightest pixels at the same position (x,y) compose the final image.

Images talk better than text. Let’s use these 3 images:

3x3 boxes with the first column white 3x3 boxes with the second column white 3x3 boxes with the third column white
img1.jpg img2.jpg img3.jpg

The lightest operation will merge these three images into a final image that will be this:

 
full

The grey pixels where fully replaced by the white pixels, since the latter are lighter. The formula to obtain the lightest pixel of an image was borrowed from this question at StackOverflow. The L is for luminance, and r, g, b are the color components of a pixel:

L = 0.2126 * r + 0.7152 * g + 0.0722 * b

The more the luminance, the lighter a pixel is. Our main idea is that if the luminance value L for a pixel is greater than the L value for another pixel, that pixel will be the lightest one and, therefore, be chosen.

I don’t know if it’s the scientifically proved best choice, but it has worked for my purposes. This is my implementation in Go:

Comparing images

At first, I created a function to receive a slice of image.Image‘s and travel through their pixels, comparing them:

In this old version, notice that the first image was read twice in the for loop and an empty slice would ruin everything. 😀 But our idea is this: store the lightest pixel and compare it to the pixel in the current image. If the newer pixel is lightest, we replace the current lightest pixel.

I created this function using TDD and it worked well with image.Image‘s, but how can we parse images from File‘s and keep the code testable?

Using containers for testing

The first version of Result() function received a slice of image.Image‘s, based on my test where I created some image.Image‘s to verify. But it has some limitations in the real world. How could I handle real files?

  • If I used a slice of image.Image‘s as arguments, I would have to get a list of files, decode them and create a very heavy slice of image.Image‘s to pass to Result().
  • If I used a slice of File‘s as parameter, it would become harder to do unit testing.

I created an interface to solve both cases: ImageContainer.

Its implementations must have a GetImage() function that will return a image.Image only when needed, so it’s a more lightweight approach. For example, a FileImageContainer would keep the file path within it and return the image.Image when GetImage() is called. An ImageItselfContainer, used in the unit tests, can keep the image data itself and returns this data when GetImage() is called. This is the current implementation:

The final version of Result(), now using an ImageContainer‘s instead of image.Image‘s, is shown below. The image operation won’t know (and it doesn’t need to know!) what kind of container it’s dealing with, and now the same code can handle image.Image‘s and File‘s!

Parsing command line arguments with cli.go

To parse command line arguments I used cli.go. It’s a library that parses command line parameters and creates a nice help output:

$ blzimg 
NAME:
   blzimg - Execute some operations on images

USAGE:
   blzimg [global options] command [command options] [arguments...]

VERSION:
   0.1

AUTHOR(S): 
   Esdras Beleza  
   
COMMANDS:
   lightest, l  Merge the lightest pixels of some images in a single one
   help, h      Shows a list of commands or help for one command
   
GLOBAL OPTIONS:
   --output "final.jpg" Output file
   --help, -h           show help
   --version, -v        print the version

Finally: running blzimg

A few days ago I took some crappy pictures just to test my new Rokinon 12mm lens with my Fuji X-E1 camera. I used a intervalometer to take these pictures with a interval of 20 seconds between them, in a total of 8 minutes that were compressed in this timelapse of a few seconds:

What if we run blzimg to merge all the images from this timelapse into a single image using the lightest operation?

blzimg --output final.jpg lightest 2015-04-07_21-*

Since the lightest points in these pictures are the clouds and the stars, the output is a giant cloud and the amazing beginning of a star trail as if it were below the clouds:

final

Show me the code!

The full code can be downloaded at my GitHub. 😀

18 Responses

  1. Paulo Vargas April 28, 2015 / 10:54

    Great idea! It is even better because of the ghosts by the cars :)
    Well done.

  2. koblas June 22, 2015 / 15:53

    That's more likely a planet than a star.

  3. Craig June 22, 2015 / 18:09

    Nice! In your gist code for image_container.go (not in your actual Git repo), line 19 should be:

    func (i ImageItselfContainer) GetImage() image.Image {
    return i.Image
    }

  4. Thomas July 3, 2015 / 4:30

    Please be aware that the Luminance weights you are using are only valid for sRGB / Rec. 709 colourspaces and they are assuming a linear input.

    Cheers,

    Thomas

  5. スーパーコピーブランド格安販売店はこちらへ!品々の激安価格に持ったスーパーコピーブランド 代引きの新作はお客様に提供されます。安心、迅速、確実、お客様の手元にお届け致します。★弊社は9年の豊富な経験と実績を持っております。★一流の素材を選択し、精巧な作り方でまるで本物のようなな製品を造ります。★品質を重視、納期も厳守、お客様第一主義を貫きは当社の方針です。★驚きの低価格で商品をお客様に提供致します!★早速に購入へようこそ! http://www.eevance.com/tokei/chanel/index.html

  6. payforessay April 26, 2016 / 3:56

    Not only your contents but also the way you present the issue is awesome. Particularly this one is articulating the sense of the discussion. Your word power is awesome. Keep it up.

  7. make a website May 19, 2016 / 6:56

    If image get command can upload from a computer then its also get link from an URL?

  8. paper writing June 12, 2016 / 14:09

    Confused about the fact whether to do paper writing by yourself or to hire someone for paper writing ? Visit us

  9. e-medsx.com August 17, 2016 / 6:54

    Thanks for the codes! The results of your program blzimg are amazing. Your experiment of running blzimg to merge all the images from the timelapse into a single image using the lightest operation is great.

  10. piano tiles February 23, 2017 / 1:50

    Everyone loves the look and feel of a clean home. Thanks for choosing this topic for post and update me. Game Stick RPG 2 looks like your real life, you can get everything you want, game Douchebag workout 2 is to renew him, so that he can hook up with the sexy girl appearing in the introduction, return man 3 saves your progress, with Potty Racers do you want to fly to Mars with me?

  11. Apental Calc Apk April 29, 2017 / 9:26

    Awesome man and codes you shared here are great. Please share more content with us.

Leave a Reply

Your email address will not be published. Required fields are marked *