Filter   <---- My Project!

What does it even mean to filter an image? You can think of filtering an image as taking the pixels of some original image, and modifying each pixel in such a way that a particular effect is apparent in the resulting image.

Grayscale

One common filter is the ā€œgrayscaleā€ filter, where we take an image and want to convert it to black-and-white. How does that work?

Recall that if the red, green, and blue values are all set to 0x00 (hexadecimal for 0), then the pixel is black. And if all values are set to 0xff (hexadecimal for 255), then the pixel is white. So long as the red, green, and blue values are all equal, the result will be varying shades of gray along the black-white spectrum, with higher values meaning lighter shades (closer to white) and lower values meaning darker shades (closer to black).

So to convert a pixel to grayscale, we just need to make sure the red, green, and blue values are all the same value. But how do we know what value to make them? Well, itā€™s probably reasonable to expect that if the original red, green, and blue values were all pretty high, then the new value should also be pretty high. And if the original values were all low, then the new value should also be low.

Reflection

Some filters might also move pixels around. Reflecting an image, for example, is a filter where the resulting image is what you would get by placing the original image in front of a mirror. So any pixels on the left side of the image should end up on the right, and vice versa.

Note that all of the original pixels of the original image will still be present in the reflected image, itā€™s just that those pixels may have rearranged to be in a different place in the image.

Blur

There are a number of ways to create the effect of blurring or softening an image. For this problem, weā€™ll use the ā€œbox blur,ā€ which works by taking each pixel and, for each color value, giving it a new value by averaging the color values of neighboring pixels.

Edges

In artificial intelligence algorithms for image processing, it is often useful to detect edges in an image: lines in the image that create a boundary between one object and another. One way to achieve this effect is by applying the Sobel operator to the image.

Like image blurring, edge detection also works by taking each pixel, and modifying it based on the 3x3 grid of pixels that surrounds that pixel. But instead of just taking the average of the nine pixels, the Sobel operator computes the new value of each pixel by taking a weighted sum of the values for the surrounding pixels. And since edges between objects could take place in both a vertical and a horizontal direction, youā€™ll actually compute two weighted sums: one for detecting edges in the x direction, and one for detecting edges in the y direction.

Implementation

Implement a program that applies the filters grayscale, reflection, blur, and edges to BMPs.

Recover   <---- My Project!

In anticipation of this problem, we spent the past several days taking photos of people we know, all of which were saved on a digital camera as JPEGs on a memory card. (Okay, itā€™s possible we actually spent the past several days on Facebook instead.) Unfortunately, we somehow deleted them all! Thankfully, in the computer world, ā€œdeletedā€ tends not to mean ā€œdeletedā€ so much as ā€œforgotten.ā€ Even though the camera insists that the card is now blank, weā€™re pretty sure thatā€™s not quite true. Indeed, weā€™re hoping (er, expecting!) you can write a program that recovers the photos for us!

Even though JPEGs are more complicated than BMPs, JPEGs have ā€œsignatures,ā€ patterns of bytes that can distinguish them from other file formats. Specifically, the first three bytes of JPEGs are

0xff 0xd8 0xff

from first byte to third byte, left to right. The fourth byte, meanwhile, is either 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, or 0xef. Put another way, the fourth byteā€™s first four bits are 1110.

Odds are, if you find this pattern of four bytes on media known to store photos (e.g., my memory card), they demarcate the start of a JPEG. To be fair, you might encounter these patterns on some disk purely by chance, so data recovery isnā€™t an exact science.

Fortunately, digital cameras tend to store photographs contiguously on memory cards, whereby each photo is stored immediately after the previously taken photo. Accordingly, the start of a JPEG usually demarks the end of another. However, digital cameras often initialize cards with a FAT file system whose ā€œblock sizeā€ is 512 bytes (B). The implication is that these cameras only write to those cards in units of 512 B. A photo thatā€™s 1 MB (i.e., 1,048,576 B) thus takes up 1048576 Ć· 512 = 2048 ā€œblocksā€ on a memory card. But so does a photo thatā€™s, say, one byte smaller (i.e., 1,048,575 B)! The wasted space on disk is called ā€œslack space.ā€ Forensic investigators often look at slack space for remnants of suspicious data.

The implication of all these details is that you, the investigator, can probably write a program that iterates over a copy of my memory card, looking for JPEGsā€™ signatures. Each time you find a signature, you can open a new file for writing and start filling that file with bytes from my memory card, closing that file only once you encounter another signature. Moreover, rather than read my memory cardā€™s bytes one at a time, you can read 512 of them at a time into a buffer for efficiencyā€™s sake. Thanks to FAT, you can trust that JPEGsā€™ signatures will be ā€œblock-aligned.ā€ That is, you need only look for those signatures in a blockā€™s first four bytes.

Realize, of course, that JPEGs can span contiguous blocks. Otherwise, no JPEG could be larger than 512 B. But the last byte of a JPEG might not fall at the very end of a block. Recall the possibility of slack space. But not to worry. Because this memory card was brand-new when I started snapping photos, odds are itā€™d been ā€œzeroedā€ (i.e., filled with 0s) by the manufacturer, in which case any slack space will be filled with 0s. Itā€™s okay if those trailing 0s end up in the JPEGs you recover; they should still be viewable.

Now, I only have one memory card, but there are a lot of you! And so Iā€™ve gone ahead and created a ā€œforensic imageā€ of the card, storing its contents, byte after byte, in a file called card.raw. So that you donā€™t waste time iterating over millions of 0s unnecessarily, Iā€™ve only imaged the first few megabytes of the memory card. But you should ultimately find that the image contains 50 JPEGs.