13,593,169 members
alternative version

#### Stats

18.7K views
39 bookmarked
Posted 29 May 2015
Licenced CPOL

# Some Cool Image Effects

, 29 May 2015
An application to create interesting effects for your favorite pictures.

## 1. Introduction

Innumerable are the methods by which images can be manipulated so as to create new ones; some of these methods are quite simple, yet create new ones which appear cool and special. A number of software applications create these cool effects, and it is fascinating to understand the inner mathematical operations of these effects. These so called mathematical operations are often simple arithmetic manipulations, easy to understand and implement; their beauty is that we can easily see the effects on the images.

We find many of these mathematical formulae scattered over the Internet; whereas others remain hidden as 'trade secrets' within commercial applications. In this article, we present a program which applies some interesting effects to your favourite images; and we also present the formulae and simple implementations to create these effects. To keep the code reasonably simple and understandable, we've not attempted any optimizations to increase speed. It is our hope that you will enjoy these cool effects, and also get an understanding of the corresponding mathematics and code.

## 2. Objective

• Explore the mathematics, rather arithmetic, behind some of these image effects.
• Enable the user to select an effect from one of these:
2. Pixelate: Someone just averages out blocks of the picture.
3. Quantize: Fifty shades of grey just merge into one.
4. Wave: A sine wave just passes by.
5. Solarize: Someone just mixes up the colours.
6. Parabolize: Yet another colour mix up.
7. X-ray: Wilhelm Roentgen stops by, and says 'Hello, how do you do?'.
8. Canvas: A weaver weaves your image on canvas.
9. Dither: A drop of ink ponders - 'Should I deposit, or not?'.
10. Edge: Everything between the edges just gets washed out.
11. Emboss: A bas-relief artist sees your picture.
12. Glass: Someone sends you a ground glass sheet.
13. Oil Paint: Not Picasso, or van Gogh, though.
14. Outline: Another edgy wash out.
• Vary parameters for these effects, and get a (near) immediate preview.
• Save the resulting image.

Some of these effects, as mentioned earlier are taken from other public-domain sources, and we acknowledge them gratefully; whereas the other effects are our own 'findings' - we will point them out appropriately.

An important aspect in our implementation is that we'd like all the code to be ours. No usage of third-party DLLs. Yes - we have a DLL in our application, but its code is entirely ours.

There's a reason for keeping the code self-contained, without external dependencies (other than Windows APIs). We see a trend among beginners / students today to use image processing packages, especially one-word commands. While this facilitates speed of learning, our impression is that it may leave gaps in understanding the implementation behind such commands. In this application, there are no 'commands'; we directly manipulate the individual R, G, B values via code.

## 3. Algorithms for these effects

We've created a PDF file containing all the formulae, along with short descriptions of the steps. This document is among the downloads above. This is intended to serve as a handy reference to the methods. We've tried to keep the descriptions simple, easy to follow, and quick to read. We've also tried to keep the description brief and to-the-point, so that in six pages, it covers all our fourteen effects. Should you find difficulty in understanding these descriptions, please write back to us via the forum.

## 4. Usage of the software

The main screen for this application is shown below:

As in an earlier article by one of the authors, we use the strategy of fitting the input image to size 600 x 600, and apply all effects on this scaled image during user interaction. Some real-life images are really large; applying the effect on such images sizes may cause user annoyance in terms of delayed response. We therefore scale to 600 x 600, and then use this for preview. This happens reasonably in real time.

We've designed the application to be reasonably intuitive, and self-explanatory. However, for completeness, we list the steps to use it:

1. Open an image by clicking on the Open Image button. A default image is available for you to play with.
2. Select any effect using the effect selection combo shown below.

3. Vary any parameters to see the effect on the right pane. Some of the effects enable you to change colours by clicking on the buttons shown below.

4. Save the resulting image by right-clicking on the right pane.

## 5. A Look at the Effects

In this section, we show the effects on a representative image. This image shown below, and is of a statue of Kempegowda, who founded our city, Bengaluru. more than 400 years ago. One of the authors recently visited the city centre, and shot this picture. This picture represents at once the older and newer aspects of our city, with a modern building on the left. Incidentally, the international airport here is named after him. With the intention of testing our cool effects of text also, we've included his name on the image, in the local language Kannada, and in English.

Now, to each of these effects:

### 5a. Sepia

Code: SepiaAlgorithm.cs. The code for computing the sepia tone is:

```    // Target image
for (el = 0; el < CurrentHeight; ++el) {
for (k = 0; k < CurrentWidth; ++k) {
index = el * CurrentWidth + k;
r = Pixels8RedCurrent[index];
g = Pixels8GreenCurrent[index];
b = Pixels8BlueCurrent[index];
// Grayscale
intensity = 0.3 * r + 0.6 * g + 0.1 * b;

// Red
if (intensity > threshold)
tone = 255.0;
else
tone = intensity + 255.0 - threshold;
dRed = tone;

// Similar code for green and blue components

}
}
}```

Here, `Pixels8RedCurrent[]` are the red pixels from the input image, whereas `pixDoubleRed[]` are the corresponding ones from the intermediate double image created. These are subsequently scaled to the range 0 - 255 to get back the sepia toned image.

For two different settings of the sepia slider, we get these images:

### 5b. Pixelate

Code: PixelateAlgorithm.cs.

The most important method here is to setup the block geometry. This is achieved using the following code. The mathematics (rather arithmetic for this is explained in the PDF file available for download).

```// Setting up the block widths and heights
void SetupBlockGeometry(List<algorithmparameter> value) {
var userBlockWidth = value.First(x => x.ParameterName == "BlockWidth");
noHorizBlocks = userBlockWidth.Value;

if (noHorizBlocks >= CurrentWidth)
noHorizBlocks = CurrentWidth / 2;

blockWidth = CurrentWidth / noHorizBlocks;
blockWidths = Enumerable.Repeat(blockWidth, noHorizBlocks).ToList();

// For the horizontal blocks
var horizList = Enumerable.Range(0, noHorizBlocks - 1).ToList();
// Shuffle the list.
Random random = new Random(Environment.TickCount);
shuffledHorizList = horizList.OrderBy(k => random.Next()).ToList();
int diffH = CurrentWidth - noHorizBlocks * blockWidth;
if (diffH > 0) {
++blockWidths[noHorizBlocks - 1];
for (int i = 0; i < diffH - 1; ++i) {
++blockWidths[shuffledHorizList[i]];
}
}

// Similar code for the vertical block size computation
}```

This is followed by averaging the colour over a block.

For two different parameter settings, we get these images:

### 5c. Quantize

Code: QuantizeAlgorithm.cs. The relevant code extract is:

```for (int i = 0; i < Pixels8RedCurrent.Count; i++) {
if (!bw) {
Pixels8RedResult[i] = (byte)((Pixels8RedCurrent[i] / sizeOfStep) * sizeOfStep);
} else {
bGray = (byte)(0.3 * Pixels8RedCurrent[i] + 0.6 * Pixels8GreenCurrent[i]
+ 0.1 * Pixels8BlueCurrent[i]);
Pixels8RedResult[i] = (byte)((bGray / sizeOfStep) * sizeOfStep);

// Similar code for the green and blue components
}
}```

This code just does a rounding-off of the pixel values to the nearest mid-point of the corresponding range. We also provide a binarize functionality.

### 5d. Wave

Code: WaveAlgorithm.cs

The relevant code extract is here (again, refer to the PDF for the math). This is a case of image warping.

```for (el = 0; el < CurrentHeight; ++el) {
y = el;
w2 = CurrentWidth * y;
for (k = 0; k < CurrentWidth; ++k) {
x = k;
if (currentSelection == 1) {
y = Convert.ToInt32(el + 20.0 * Math.Sin(2.0 * Math.PI * k / 128.0));
}

// Source pixel
w1 = CurrentWidth * y + x;
r = Pixels8RedCurrent[w1];

// Target pixel
w1 = CurrentWidth * el + k;
Pixels8RedResult[w1] = r;

// Similar code for the green and blue components, and for the other cases
}
}```

### 5e. Solarize

Code: SolarizeAlgorithm.cs

The relevant code is:

```for (el = 0; el < CurrentHeight; ++el) {
w2 = CurrentWidth * el;
for (k = 0; k < CurrentWidth; ++k) {
w1 = w2 + k;
r = Pixels8RedCurrent[w1];

if (r > threshold)
r = (byte)(255 - r);

Pixels8RedResult[w1] = r;
// Similar code for green and blue components
}
}```

This involves some kind of inversion on the pixel values, depending on the user-specified threshold.

### 5f. Parabolize

Code: ParabolaAlgorithm.cs

Code is similar to solarize, but with a different formula. Refer to the PDF notes.

### 5g. X-Ray

Code: XRayAlgorithm.cs

This is an easy algorithm, whose code is:

```for (el = 0; el < CurrentHeight; ++el) {
w2 = el * CurrentWidth;
for (k = 0; k < CurrentWidth; ++k) {
// Source Pixel
index = w2 + k;
r = Pixels8RedCurrent[index];
g = Pixels8GreenCurrent[index];
b = Pixels8BlueCurrent[index];
dGray = 0.3 * r + 0.6 * g + 0.1 * b;

// Code for clamping the pixel values ...
// Compute the negative grayscale value
dGray = 255 + factor - dGray;
// factor is a magic number

byteGray = (byte)dGray;
SetBackgroundColour(colour, index, byteGray);
}
}```

### 5h. Canvas

Code: CanvasAlgorithm.cs

As written in the PDF file, this and all subsequent algorithms are non-scalable algorithms.

This algorithm makes use of two images, one of which is a texture image. Both the input image and the texture image are composited to create the new one.

```if (bVal < 128) {
resultRed = (byte)((bVal * r) >> 7);
} else {
resultRed = (byte)(255 - (((255 - bVal) * (255 - r)) >> 7));
}
// Similar code for green and blue components```

Here, `bVal` comes from the texture image, whereas r, g and b are from the input image. This is one way of compositing; there are many other ways possible.

### 5i. Dither

Code: DitherAlgorithm.cs

This makes use of a dither matrix, ten of which are provided in the code.

```for (int j = 0; j < height; ++j) {
start = width * j;
for (int i = 0; i < width; ++i) {
i1 = i + start;
threshold = ditherMatrix[methodValue.Value - 1, j % n, i % n];
if (PixGray[i1] > threshold) {
SetBackgroundColour(colour, i1, value);
} else {
SetBackgroundColour(colour, i1, 0);
}
}
}```

Here, `pixGray` is the grayscale value.

### 5j. Edge

Code: EdgeAlgorithm.cs

This is quite an elaborate algorithm, which is explained in detail in the PDF notes. The relevant code extract is:

```ComputeGrayscaleImage();
ComputePixGrayAverage(CurrentHeight, CurrentWidth);
ComputeDxAndDyImages(CurrentHeight, CurrentWidth);
ComputeThetaAndMagnitudeImages(CurrentHeight, CurrentWidth);
ComputeMaxAndMinMagnitudeImage();
CreateFinalImage(CurrentHeight, CurrentWidth, threshold);```

### 5k. Emboss

Code: EmbossAlgorithm.cs

This uses an emboss matrix, and is explained in the PDF Notes. Five such emboss matrices are provided. The relevant code extract is:

```ComputeGrayscaleImage();
ComputeDoubleImage();
ComputeMaxAndMinDoubleImage();
CreateFinalImage(colour);```

### 5l. Glass

Code: GlassAlgorithm.cs

This is explained in detail in the PDF Notes. A pixel at location (i, j) is replaced by another within a user-specified neighbourhood of this pixel. Looking at how the code works is left as an exercise to you.

### 5m. Oil Paint

Code: OilPaintAlgorithm.cs

The math for this also explained in the PDF Notes. Essentially, a pixel at location (i, j) is replaced by the most frequently occurring pixel value within a neighbourhood.

### 5n. Outline

Code: EdgeAlgorithm.cs

This is a special case of the Edge algorithm mentioned above.

## 6. Usage of the MVVM Paradigm

The initial version of this application was based on WinForms, where the algorithmic and presentation aspects were very tightly coupled; this was never published. We decided to convert this to WPF, and redesign to MVVM. Among other things, this enables reuse of the Algorithm part.

The three components have the following responsibilities:

1. View: The UI appearance without application logic. For example: `CoolImageEffects.ImageProcessingView.`
2. ViewModel: Preparation of the data from the model for viewing, and linking workflow actions (logic) to the UI. For example ` CoolImageEffects.ImageProcessingViewModel`.
3. Model: Gateway to the domain entities and logic. For example ImageSource, Effects and Algorithm options.

With this MVVM paradigm, we decouple Presentation Logic from the Algorithm part. There are two projects in our application:

1. Algorithm : Contains the domain entities and algorithm processing logic. This creates a DLL.
2. CoolImageEffects: Contains view and view-model components. This is the executable part.

All these are encapsulated in a Visual Studio 2010 solution.

## 7. More on the Code

As mentioned earlier, the code is organized into two projects.

### 7a. Algorithm Project

This is the core of the application. The following is the organization of files within this project.

The most important set of files here are:

1. `ImageData.cs`: which stores the image attributes (width, height), and the image buffers (R, G, B pixel values). Additionally, it has methods common to all images for loading, saving, setting background colour, transforming to grayscale, etc.
2. `AlgorithmBase.cs`: which serves as the abstract base class for all algorithms. It declares the method ApplyEffect, which is implemented in its child classes.
3. Algorithm implementations, which implement the actual cool effect algorithms.

### 7b. CoolImageEffects Project

This is the application part. The organization of files here is:

Important folders here are:

1. Converter: where the various conversions are done.
2. View: which houses the views appropriate to the different cool effects.
3. ViewModel: where the view models are housed.

As the focus here is more on the algorithmic part, we don't describe this in a greater detail.

### 7c. Including your own effect

You may come up with a nice and cool effect, and may want to try it out. This application provides that facilty for you, provided you are good at C# programming. The steps to do so are:

1. Add your class to Algorithm project. Derive it from `AlgorithmBase`. If you look at this class, you'll find two set of variables: `Pixels8nnnCurrent` and `Pixels8nnnResult`, where nnn is one of Red, Green or Blue. The `...Current` variables are pixel buffers from the input image, whereas the `...Result` variables are pixel buffers from the output image. These are linear buffers.
2. Implement the methods `ApplyEffect`, `GetOptions` and `GetDisplayInfo` within that class. Study the code of any algorithm, implement accordingly.
3. Add the name of your algorithm to the `Effects` enum.
4. Instantiate your algorithm and add to dictionary `availableAlgorithms` in `ImageProcessingAlgorithm` class.

Since we've provided the platform for you to experiment your cool effect, we'd appreciate if you can also make available your nice cool effect to the community at large.

1. Source code as a zip file.
2. Executable as a zip file.
3. PDF documentation as a zip file.

## 8. Points of Interest

1. If you've glanced at the PDF document in the downloads, you'll notice two types of effects - Scalable and Non-scalable. Scalable means that there is no dependence on image size - two images with the same 'picture', with different sizes yield identical results. This is not so with the non-scalable effects; the user may see an effect on the screen, and when saving on the original image, may visually notice an entirely different effect. To remedy this to some extent, we provide two saving options - saving the original image, and saving the scaled image. These are available via context menu on the right hand image pane on the screen.
2. "This application has no bugs" - we believe we've matured enough not to make such a claim. Though we've tested using different image types and sizes, there's a non-zero possibility that your image and user interaction may take the program flow through untested territory. Do send us feedback on this and we'll rectify.
3. "This is the most optimal implementation of MVVM" - this is yet another claim we'd not like to make. Yes - this is a solution which works, is reasonably optimal, and creates near real-time effects for the user. Further, some parts of the code are intentionally kept sub-optimal for easy understanding by beginners. For example, the Oil Paint algorithm has two separate methods for 3 x 3 and 5 x 5, apparently duplicating code; though these can be unified, a beginner may find it difficult to understand. For us, understandabilty of the code, especially the algorithm part is important. However, let this not deter you from providing inputs on the code.

## 9. Closure

We find these effects to be cool, and have therefore named the application accordingly. We believe that you'll also find them cool enough. We hope you'll enjoy this application, and benefit from its effects. Show this to children at home, make them play with it, and let us know their feedback.

Among all these effects we've encapsulated in this small application, which do you like the most? What other parameters could be added to that effect, to make it more appealing to you? What other effects would you like to get added? Please write on these aspects too.

As mentioned earlier, the algorithms for these effects, are not new. However, the packaging is new. And the encapsulation of these effects into a self-contained code base, hopefully not too difficult to follow for a beginner, is perhaps new. The code base will also allow you to add new cool effects without too much of a hassle. If you feel happy and satisfied after viewing and saving your images, do let us know. Even otherwise, we appreciate your feedback.

## 10. Thanks

This application was developed during spare time, while we are employed at Philips India; with the intention of learning while building a simple image processing application. We express our sincere thanks to the Director of our Department and to Philips Intellectual Property and Standards Department for allowing us to publish this article online.

## History

• Version 1.0: 29 May 2015.

## Share

 Architect India
Programming computers since about 1987, my first computer language was Fortran 77. Later I learnt C, C++ and C#. Also programmed a little in VB .Net. Worked with Enterprise Java for a short while. Currently, interested in image processing. I love watching Kannada movies, and listening to Kannada songs. Currently studying and understanding the Bhagavad Geetha.

Looking for opportunities in Bangalore. Please write to me at amarnaths dot codeproject at gmail dot com.

 India
No Biography provided

 Software Developer India
No Biography provided

 India
No Biography provided

## You may also be interested in...

 First Prev Next
 Grayscale images descartes8-Nov-15 13:35 descartes 8-Nov-15 13:35
 Re: Grayscale images Avijnata10-Nov-15 1:51 Avijnata 10-Nov-15 1:51
 thanks Hooman_Kh10-Aug-15 14:25 Hooman_Kh 10-Aug-15 14:25
 Re: thanks Avijnata10-Aug-15 14:57 Avijnata 10-Aug-15 14:57
 Re: Great work Avijnata17-Jun-15 1:19 Avijnata 17-Jun-15 1:19
 Re: My vote of 5 Avijnata15-Jun-15 21:36 Avijnata 15-Jun-15 21:36
 My vote of 5 DrABELL13-Jun-15 15:36 DrABELL 13-Jun-15 15:36
 Re: My vote of 5 Avijnata13-Jun-15 19:50 Avijnata 13-Jun-15 19:50
 Re: My vote of 5 DrABELL14-Jun-15 5:26 DrABELL 14-Jun-15 5:26
 Good work Nish Nishant5-Jun-15 6:27 Nish Nishant 5-Jun-15 6:27
 Re: Good work Avijnata5-Jun-15 6:44 Avijnata 5-Jun-15 6:44
 My vote of 5 Champion Chen31-May-15 17:13 Champion Chen 31-May-15 17:13
 Re: My vote of 5 Avijnata1-Jun-15 3:47 Avijnata 1-Jun-15 3:47
 Great article! Arthur Dumas29-May-15 13:28 Arthur Dumas 29-May-15 13:28
 Re: Great article! Avijnata29-May-15 14:02 Avijnata 29-May-15 14:02
 Nice work! Ravi Bhavnani29-May-15 10:16 Ravi Bhavnani 29-May-15 10:16
 Re: Nice work! Avijnata29-May-15 14:02 Avijnata 29-May-15 14:02
 Last Visit: 31-Dec-99 18:00     Last Update: 20-Jun-18 19:27 Refresh 1