Image Processing Algorithms (Using R)

Below are some image processing tests I tried with R. I built a couple of tools that could help to process and present images which I placed below. The tools include functions to:

Together with the statistical capabilities of R, it could be a great free option for image processing. It might be a bit slow but definitely can do the job. I didn't spend a lot of time on optimization and debugging and there are some known issues. If anyone find any issue or optimization that could be fixed, I would be happy to know. Feel free to use any of this code on your own risk.
The R tools used could be found here: imageProc.R

Here are few examples.
A lot of math knowledge is needed such as histograms, convolution, Cosine and Fourier transform, Graph Cuts, Beleif Propagation, PDE and more.
The algorithms are from multiple sources and are not explained here.

1. Loading and Plotting an Image

There are libraries in R for reading and writing images. I used the jpeg and bmp libraries.
library(bmp);
library(jpeg);
picbmp <- read.bmp("pic.bmp");
picjpg <- readJPEG("pic.jpg");

# For BW pics with 3 channels only one of the matrices could be manipulated
pic <- picbmp[,,1]; 
# Transform the pixles to the range of 0:1
pic <- (pic-min(pic))/(max(pic)-min(pic));
To plot an image and histogram I wrote some wrapper functions for 1 and 3 channel plot and histogram. It also get the asp to control if to keep the aspect-ratio and rem_mar to control if to remove margins (both TRUE in default).
plotImg(pic, asp=TRUE, rem_mar=TRUE);
title(main="Any Title");
plotImgHist(pic);

2. Image equalization

This example takes an image and equalize the color histogram by a map function of the cumulative distribution function (CDF) to the pixel levels. R already support an estimaged CDF function so this is straight forward. After equalizing any distribution can be reached by applying the distribution to the equalized image. In this example, I transformed to the beta distribution.
# equalize image
eqimg <- eqImg(pic);
# apply beta distribution to the image
betaimg <- qbeta(eqimg, 2,3);

Equalized BW click for full size

Equalized BW click for full size


3. Filter Masks

A filter mask is a convolution between an image and a mask. The image is processed in pixels (=1x1 block). For each block a mask is applied FUN=convProc. The type of mask is currently defined as a global variable imageProcMask.
# Emboss
imageProcMask <<- emboss;
iemboss <- imageProc(pic, 1, convProc);

Equalized BW click for full size


4. Image Sharpening

In this example (c) is a sharpen mask applied to the image. (d) is unsharp mask of reducing the laplacian from the original image.
# (c) Sharpen mask
imageProcMask <<- sharpen3;
isharp <- imageProc(picblur, 1, convProc);

# (d) Unsharp mask
imageProcMask <<- edge3; 
lp <- imageProc(picblur, 1, convProc);
iusm <- 1.6*picblur - 0.4*lp;

Equalized BW click for full size


5. Noise Cancelation

After adding black and white pixel noise to the image, two kind of cancelation methods were applied: (c) is a blur filter mask - which should work better for a gausian noise. (d) is a median filter - which produces great results for this kind of noise.
# (c) Blur
imageProcMask <<- blur;
iblur <- imageProc(npic, 1, convProc);
# (d) Median Filter
imed <- imageProc(npic, 1, medianProc);

Equalized BW click for full size


6. Motion Cancellation using Wiener Filter

Motion cancellation assumes knowing of the motion function. This example works also in the frequency domain. (b) is the image after applying a motion mask. A motion is simulated by integrating pixels along the motion path. A 21x21 mask was used with a horizontal line of ones. (e) is the Inverse Fourier Transform of the Fourier Transform of the motioned image (G) divided by the Fourier Transfrom of the motion (H). Without any noise it result with the exact original image. But it's very sensitive to noise. By adding a small noise, the result is awful. (f) is the result of applying a Wiener Filter

K is taken as the noise SD. The result of the Wiener Filter which is minimizing the MSE is great.
H <- fft(h);
G <- fft(g);
Fe <- Conj(H)*G/(Mod(H)^2+K);
fe <- Mod(fft(Fe,inverse = TRUE));

Equalized BW click for full size

Equalized BW click for full size


7. Image Compression

This test simulates the JPEG comression. It applies a DCT to 8x8 blocks of the image, weighted quantization of the result, removes the trailing zeros, calculates the binary Huffman code and the size of the final image. The result of different levels of quantizations is shown. With level one compression the result is pretty similar to the original image. L3 compression losses parts of the details and L5 losses much more details. This code is used for the 8x8 block DCT and quantization.
# Setup quantization matrix - use mult for quantization level
imageProcQM <<- mult*imageProcQM.default; 
	
# perform dct on image
ip <- imageProc(pic, 8, dctBlockq)

# Decompress Image
ipi <- imageProc(ip, 8, idctBlock)

Equalized BW click for full size


8. Image Segmentation

The goal of this test is to separate the foreground from the background of an image. The image I used is relatively complex since the background have many colors and the foreground center is different from the leaves. If we try to segment the image using Otsu's method, the foreground and background are mixed up. Otsu's threshold the image in level 100.
# Find the between class point using Otsu's method
h <- hist(pic24, breaks=seq(0,256), plot=F)
mp <- otsu(h$count)
# partition the graph
parn1 <- which(pic24 < mp)
parn2 <- which(pic24 >= mp)

Otsu's segmentation click for full size

Using Graph Minimum Cut get much better result. A graph is created from the image where every pixel is a vertex in the graph. We create two more vertices for the source and target. Each vertex is connected only to its neighbours. The Edges are assigned with capacity corresponding to the difference between the pixels levels according to

The user marks areas which belong to the forground or background. We add the marked foreground nodes to the source and background nodes to the target with high capacity. Then we calculate the min cut of the graph and after smoothing the edges, partition the image. We can also connect some other (picked or random) pixels to the source and target with capacity corresponding to the probability of that pixel according to the foreground or background histogram. However, this could have some risk in creating islands in the foreground or background.

Otsu's segmentation click for full size


9. Image Completion

The goal of this test is to complete unknown parts of an image. The way it's done is to copy patahces from known parts to the missing parts. The Statistics of Patch Offsets for Image Completion have very good results. In the example, the foreground removed from the image (a),(b),(c). Then the most dominant patches are found (e), (f). Belief Propagation is used to find the rigth patches to complete the background (g), (h). The result looks very nice to the eye. It's smooth and no patterns are seen as opposed to the Photoshop Content Aware Fill (d).

Image Completion click for full size


10. Active Contours

The goal of this test is to fit a contour to an image. It starts from a curve in step#0 where it evolves over time using the following Partial Differential Equation (PDE):

Active Contours click for full size

After few steps, the contour fits the original image curve.




Write a comment...
comments powered by Disqus