Pixel mapping and hot pixels

Hot pixels are bright pixels that show up in images from digital cameras. Most digital cameras produce them but they don't show up unless you use the camera with manual settings and a long capture time. When using a digital camera in Auto mode it recognizes when these pixels are likely to show and will usually apply a noise reduction algorithm to the image data before writing the data to you memory card. This time can be measured in seconds and increases signficantly the time before you can take your next photograph. For this reason many digital camera manufacturers usually provide the configuation option to turn noise reduction off.

If you are like me though, I tend to use my digital camera in manual mode nearly all of the time. I usually forget to turn noise reduction on when working with long capture times (film camera users know this as shutter speed) and I end up with some of my pictures containing a number of hot pixels.

I decided to add the ability to remove these hot pixels mostly for my own benefit, and the reason it had not appeared until the free release was because this feature jumped above other ideas that might have been more compelling if I was still trying to sell iMagine Photo.

Hot pixels are sometimes just known as bad pixels, but bad pixels is a broader term that also includes pixels that stay dark even when they should be bright.

To be able to remove hot pixels from your images you can either apply a generic noise reduction algorithm or you can determine where the hot pixels are and then remove the hot pixels. iMagine Photo allows you to determine where the hot pixels are and allows you to use bilinear interpolation to remove the hot pixels. Each camera has different hot pixels. Each cameras hot pixels stay fairly constant over time. The longer the capture time the more hot pixels you are likely to have in your digital photos. The process described below describes the process that I have set up for myself to remove hot pixels.

I have never really played with the ISO setting of my digitial camera so the following procedure is based around exposure time only and a constant ISO setting.

Firstly I took a number of images using my digitial camera with capture times of (1/15, 1/8, 1/4, 1/2, 1, 2, and 4 seconds) all with the lens cap left on the camera. If you can't leave the lens cap on then find some way to take pictures so that no light can enter the camera.

Once I had taken these photos I rendered the photos and mapped the bad pixels using the following AppleScript.

You can open the following script in your script editor by clicking here. On my G4 1GHz Titanium Powerbook and a 4 mega pixel digital image the get bad pixels line takes about 50 seconds to finish.

on run
set thisFile to choose file with prompt "Select an image file to map hot pixels:"
tell application "iMagine Photo"
--set thisImporter to import graphic file "Kevin:Mac OS 9:Temp pictures:DSCN2059.JPG"
set thisImporter to import graphic thisFile
if the last operation error of thisImporter is not equal to 0 then
return
end if
set {x, y, xDim, yDim} to the natural bounds of thisImporter
set thisDocument to make new window document with properties {dimensions:{xDim, yDim}}
set the drawing destination of thisImporter to thisDocument
draw thisImporter
tell thisDocument
set badPixelRects to get bad pixels using rules {adjacent to:3, different by:5000, color channels:any channel, bounds rectangle:{0, 0, xDim, yDim}}
end tell
close thisDocument
close thisImporter
end tell
badPixelRects
end run
 

When I run the above script in Script Editor the above script produces the result: {{829, 1269, 832, 1272}} and indicates that I have 4 hot pixels in the contained rectangle from an image taken with a capture time of 1/15 of a second. The four numbers in the brackets represent left edge, top edge, right edge, and bottom edge of the rectangle respectively. However when applying the bilinear interpolation composition element it was necessary to increase the rectangle to cover a larger area because there is some bleeding from the hot pixels into the surrounding pixels. The hot pixels also produce some jpeg artefacts. After a bit of trial and error I found that the rectangle that best removed the hot pixels is: {{827, 1269, 833, 1275}}

I then use the above script on each of the images taken with specified exposure times.

For the image taken with a capture time of 1/8 of a second I get the result: {{829, 1269, 832, 1273}}, and I found that {{827, 1269, 834, 1275}} best removed the effects of the hot pixels.

For the image taken with a capture time of 1/4 of a second I get the result: {{1368, 19, 1371, 22}, {829, 1269, 834, 1275}}, and I found that {{1367, 19, 1372, 23}, {827, 1269, 834, 1275}} best removed the effects of the hot pixels.

For the image taken with a capture time of 1/2 of a second I get the result: {{1367, 19, 1372, 23}, {339, 945, 342, 948}, {826, 1268, 835, 1275}, {1782, 1413, 1785, 1416}}, and I found that {{1367, 19, 1372, 23}, {338, 945, 342, 950}, {826, 1268, 835, 1277}, {1780, 1413, 1788, 1419}} best removed the effects of the hot pixels.

and I continued this way.

1 second: {{1365, 19, 1374, 28}, {657, 167, 662, 173}, {338, 944, 342, 951}, {825, 1268, 836, 1277}, {1780, 1413, 1787, 1419}} removed the effects of the hot pixels.

2 seconds: {{1364, 18, 1374, 27}, {1480, 108, 1485, 113}, {655, 166, 664, 174}, {337, 943, 344, 951}, {341, 950, 342, 951}, {825, 1268, 836, 1277}, {1779, 1412, 1788, 1421}} removed the effects of the hot pixels.

4 seconds: {{1575, 9, 1579, 13}, {1364, 18, 1374, 27}, {1480, 108, 1486, 114}, {655, 166, 664, 174}, {1253, 389, 1257, 392}, {394, 704, 396, 706}, {336, 942, 345, 952}, {825, 1268, 836, 1277}, {1779, 1412, 1789, 1421}, {63, 1620, 66, 1623}}removed the effects of the hot pixels.

The process to here took me about 4 hours. However once at this point and using the following script I can remove the hot pixels by just running the following script, which can be opened in Script Editor by clicking here.

-- There is nothing copyright about this script, the copyright message is only there to demonstrate how to add a copyright message to the exif data.

property createWindowDocument : true -- if false then creates a graphic document which processes image files without displaying them.

property theAuthor : "Kevin Meaney"

property theCopyright : "Copyright Kevin Meaney 2005, Oxford, UK"

property beRecursive : true -- set to false if you dont want to process files in subfolders.

property FifteenthOfSecBadPixelRects : {{827, 1269, 833, 1275}}

property EigthOfSecBadPixelRects : {{827, 1269, 834, 1275}}

property QuarterOfSecBadPixelRects : {{1367, 19, 1372, 23}, {827, 1269, 834, 1275}}

property HalfOfSecBadPixelRects : {{1367, 19, 1372, 23}, {338, 945, 342, 950}, {826, 1268, 835, 1277}, {1780, 1413, 1788, 1419}}

property OneSecondBadPixelRects : {{1365, 19, 1374, 28}, {657, 167, 662, 173}, {338, 944, 342, 951}, {825, 1268, 836, 1277}, {1780, 1413, 1787, 1419}}

property TwoSecondBadPixelRects : {{1364, 18, 1374, 27}, {1480, 108, 1485, 113}, {655, 166, 664, 174}, {337, 943, 344, 951}, {341, 950, 342, 951}, {825, 1268, 836, 1277}, {1779, 1412, 1788, 1421}}

property FourSecondBadPixelRects : {{1575, 9, 1579, 13}, {1364, 18, 1374, 27}, {1480, 108, 1486, 114}, {655, 166, 664, 174}, {1253, 389, 1257, 392}, {394, 704, 396, 706}, {336, 942, 345, 952}, {825, 1268, 836, 1277}, {1779, 1412, 1789, 1421}, {63, 1620, 66, 1623}}

on run

--swap the commented out lines below if you want to process files in a folder instead of just a single file

--set theFolder to choose folder

--MyPrivateOpen({theFolder})

end run

 

on ProcessFile(inFile, outfile)

end ProcessFile

 

on RemoveBadPixels(thisImporter, exifData)

end RemoveBadPixels

 

on OpenGraphicsImporter(inFile)

end OpenGraphicsImporter

 

on CreateDocument(theDimensions)

end CreateDocument

 

on GetExposureTime(exifData)

end GetExposureTime

 

on GetBadPixelsRects(exposureTime)

end if

end GetBadPixelsRects

 

on AddCopyrightAndAuthorToExifData(copyrightMessage, authorName, exifData)

end AddCopyrightAndAuthorToExifData

 

on MyPrivateOpen(DroppedFiles)

end MyPrivateOpen