Geo @ ObjectGraph

Earth and Environmental Science Blog

Hey there! Thanks for dropping by my blog! Take a look around
and grab the RSS feed to stay updated. See you around!

Introduction

I recently had a chance to go over satellite image and remote sensing topics in a GIS class. I remember that I did similar thing in Java in 10 years ago but I’m re-writing the logic in python for learning purpose. The core part of code is very simple, probably it’s only 30-40 lines. So, if you are interested in how the image filter works, and how to implement in a program, please take a look at my article below.

Let’s see some examples.

Examples



Original Image



Low-Pass - Image gets blur



High-pass - Image gets sharpen

What is image filter?

How image filters look like? They are just 3 X 3 arrays. Different filter contains different numbers. The 3 X 3 array is called kernel. High-pass filter just applies a kernel (window) on the entire image.

The kernel looks like this:

kernel_high = [
    [-1.0/8.0,-1.0/8.0,-1.0/8.0],
    [-1.0/8.0,16.0/8.0,-1.0/8.0],
    [-1.0/8.0,-1.0/8.0,-1.0/8.0]
]

This 3 X 3 matrix walk through the entire picture pixel by pixel and calculate a new pixcel in the center based on those coefficients and surrounded pixels.

Simple Calculation

Let’s assume you have gray scale image. Individual pixels are represented as an integer value 0 – 255. 0 is darkest color (black) and 255 is the brightest color (white). For example, 512×512 image holds 512×512=262,144 pixels.

Below is a sample 3×3 pixels and we need re-calculate the center value (29). Take one left-top corner value from the previous array (-1.0/8.0) and multiply by 140 from the actual pixel below.

[
    [140,38,200],
    [91,29,18],
    [211,120,10]
]

Repeat same steps for rest of cells. After calculate all pixels, add them up and place it in the center. To complete entire picture, you need to do the process for 262,144 pixels.

Basically, that’s it. It’s just re-calculating the center pixel based on surrounded pixels. Let’s see the actual python codes to do this. I did not want to use any external Library like PIL or numpy to do this so that anyone can run this code. This code is learning purpose and I wrote it for readability rather than the performance.

Download and Sample Code


Download files: image_filter.zip (1.1 MB)

filter.py

 
import struct
 
#-----------------------------------------------------------------------------------------------
#read header information - width, height, data size, the beining of the data (offset)
print 'reading before.bmp and writing after.bmp'
 
fin = open("before.bmp", "rb")
print 'identifier', fin.read(2)  
print 'file size', struct.unpack('i', fin.read(4))
print 'reserved', struct.unpack('i', fin.read(4))
offset = struct.unpack('i', fin.read(4))[0]
print 'data offset',offset
print 'header size',struct.unpack('i', fin.read(4))
width = struct.unpack('i', fin.read(4))[0]
print 'width',width
height = struct.unpack('i', fin.read(4))[0]
print 'height',height
 
#-----------------------------------------------------------------------------------------------
#back to zero and write header section which is copied from the original file
fin.seek(0)
header = fin.read(offset)
 
#-----------------------------------------------------------------------------------------------
# build data array
arr = []
count = 0
for i in range(0,width):
	row = []
	for j in range(0,height):
		tmp = fin.read(1)
		val = struct.unpack('c', tmp)[0]
		row.append(val)
	arr.append(row)
 
#-----------------------------------------------------------------------------------------------
# write header information
fout = open("after.bmp","wb")
fout.write(header)
 
#-----------------------------------------------------------------------------------------------
#apply filter and write data section
 
 
#low - pass
kernel_low = [
			[1.0/9.0,1.0/9.0,1.0/9.0],
			[1.0/9.0,1.0/9.0,1.0/9.0],
			[1.0/9.0,1.0/9.0,1.0/9.0]
		 ]
 
#high - pass
kernel_high = [
			[-1.0/8.0,-1.0/8.0,-1.0/8.0],
			[-1.0/8.0,16.0/8.0,-1.0/8.0],
			[-1.0/8.0,-1.0/8.0,-1.0/8.0]
		 ]
 
##################################################################################
# change depend on the filtering method
#ker = kernel_low
ker = kernel_high
##################################################################################
 
for i in range(0,width):
	for j in range(0,height):
		val = ord(arr[i][j])
		if i!=0 and j != 0 and i < width-1 and j < height-1:
			val =  ker[0][0] * ord(arr[i-1][j-1]) + ker[0][1] * ord(arr[i-1][j])   + ker[0][2] * ord(arr[i-1][j+1]) 
			val += ker[1][0] * ord(arr[i][j-1])   + ker[1][1] * ord(arr[i][j])   + ker[1][2] * ord(arr[i][j+1]) 
			val += ker[2][0] * ord(arr[i+1][j-1]) + ker[2][1] * ord(arr[i+1][j]) + ker[2][2] * ord(arr[i+1][j+1])
			if (val < 0):
				val = 0
			if (val > 255):
				val = 255
		fout.write(struct.pack('c',chr(int(val))))
 
#closing
fin.close()
fout.close()

Closing Comments

I also sited this website from NASA:

http://idlastro.gsfc.nasa.gov/idl_html_help/Filtering_an_Imagea.html

They also explain Edge Detection (Laplacian Filtering):

[
    [0,1,0],
    [-1,4,-1],
    [0,-1,0]
]


Edge Detection

 

You can leave a response, or trackback from your own site.

Leave a Reply