Brighten: Provided with base code
Extract Channel
Crop
Quantize
Random noise
Random salt and pepper noise (an extra option for noise)
Contrast
Saturation
Sharpen
Random dither
Blur
Edge detect
Edge detect with Sobel filter (a better edge detect)
Floyd-Steinberg dither
Scale
Rotate
3 Sampling methods: point, bi-linear and Gaussian
Ordered dither: Fully functioning
Lossy Compression: I implemented binary ppm file write/read with 1 bit to 8 bit image data packing. Combined with Floyd Steinberg dithering, it can preserve visually equivalent image with 7x compression from the ASCII ppm.
Nonphotorealism:
Mosaic: Downsample the image by n, then resize the image back to original size with n x n squares. Then blur the image.
Charcoal Paint: Convolute image edges with a naive filter. I also tried erosion and dilation, but it didn't create the effect I hoped for.
Art Contest Image: Combing some implemented features, I created sketch, charcoal paint, paint1, paint2 artistic versions of the original images
image.cpp, image.h, main.cpp
Makefile
./shellScripts/*.sh
test.sh
test_compression.sh
make
make clean
Download all codes in the root directory + /shellScripts + /SampleImgs +/comressionTestImgs
chmod +x test.sh
chmod +x test_comression.sh
./test.sh
./test_compression.sh
./image -input <PATH>/<IMG.XXX> -output <PATH>/<IMG>.ppm
or
./image -input <PATH>/<IMG.XXX> -ppm_depth <#BITS> -output <PATH>/<IMG>.ppm
or
./image -input <PATH>/<IMG.XXX> -FloydSteinberg <#BITS> -ppm_depth <#BITS> -output <PATH>/<IMG>.ppm
The last option will give the best compressed image quality.
./image -input <PATH>/<IMG>.ppm -output <PATH>/<IMG.XXX>
./image -input <PATH>/<IMG.XXX> -mosaic <n> -output <PATH>/<IMG.XXX>
recommend n>10
./image -input <PATH>/<IMG.XXX> -charcoalPaint -output <PATH>/<IMG.XXX>
To run crop from command line: ./image -input ./sampleimgs/stromanthe.jpg -crop 100 150 250 350 -output ./out/stromanthe/stromanthe_crop_-1_-1_100_200.jpg
My implementation handles out of range parameters by truncating to the size of the image, as shown in below sample results.
Original Image
-1_-1_150_150
200_150_1000_1000
100_150_250_350
Point Sampling
Bi-linear Sampling
Gaussian Sampling
In the case of significant down sampling, point sampling and bi-linear sampling start to show artifacts, while Gaussian sampling gives the best image quality.
5 x 5 Scale
5 x 5 Scale
5x5 Scale
In the case of up sampling (scaling up), bi-linear has the best performance as shown in the moon images above and the edges shown below.
Updated 3/2: 10x scale with corrected Gaussian Sampling method
I implemented binary ppm file write/read with 1 bit to 8 bit image data packing. Combined with Floyed Steinberg Dither. I can achieve compressed images that are visually indistinguishable from the original image at 4 bits. And the size of the .ppm file is 14% of the original file.
However, my compression implementation is only a first stab. In the future I would like to implement what the professor had shown in class: the LCC or YUV color scheme with efficient information sampling (L/Y at 8 bit with information for each sample, while CC/YY at 1~2 bits and information for each 8x8 block). Then pack the information into my binary file.
Sketch-Eagle
Eagle-Painting
Charcoal Paint -Neighborhood
Painting 2 - Neighborhood
Sketch -Neighborhood
Painting 1 - Neighborhood
This was a fun project for me. The visual aspect of the project makes it especially appealing. The data structure of the component/pixel/image class makes it easy to implement features. I love how clean the data is structured. I also love how the project makes me implement what the professor had talked about in the class and not everything is spoon fed to us. It had just the right amount of challenge for me.
The simple features such as crop, quantize, and extract channel did not take effort to implement. Most other features took me some time, either because of a simple miscalculation/bug in my initial attempt or the corner/border cases require some thinking. The part that took me the most effort is the binary file implementation for lossy compression. I ended up spending a half day figuring out how to pack a varying number of bits into a byte. Given time, I would like to further my lossy compression implementation by implementing the LCC color scheme and fix L at 8 bits, and the other two channels at 1 or 2 bits, before packing bits into the binary file. Another idea I would like to explore in lossy compression is to process the images in the frequency domain to take advantage of most images' limited bandwidth representation. I also spent a fair amount of time on trying to come up with an authentic way to create an artistic representation of the image, and most of my attempts fell short.
For the algorithms implemented, it is amazing how well dithering preserved information during quantization. Among the 3 sampling methods, Gaussian is the best filter for down sampling and bi-linear is the best filter for up sampling. To create artistic representation of an image, I found no method is one size fits all. What makes one image look good, may look too grainy or too dark or too ... on another image. I picked some of the better ones for the contest.
Overall, this was a very fun and useful project for me and really helped me grasp the concepts Dr. Guy had taught us.
3/2 update: During Dr. Guy's office hour, I realized that my Gaussian sampling method was not correctly implemented. In my original implementation, my Gaussian sampling filter will always center at the center of a pixel. This method works fine for down sampling, but for up sampling, it ended up sampling the same pixels with the same weights for a block of pixels. This is the main reason for the poor performance of the Gaussian sampling method on large up sampling. I re-implemented the Gaussian sampling method, The down sampling results does not change much, but the up sampling performance of the new gaussian filter is as good as the bilinear method.