In this assignment I was tasked with implementing basic methods involved in 2D image processing. These methods include the following:
- Extract Channel: Leave specified rgb channel intact and set all others to zero.
- Crop: Extract a subimage specified by a corner and area.
- Quantize: Change the number of bits per channel of an image, using simple rounding.
- Random noise: Add noise to an image. A larger parameter value should make the image noisier.
- Contrast: Change the contrast of an image.
- Saturation: Change the saturation of an image.
- Blur: Blur an image by convolving it with a Gaussian low-pass filter.
- Sharpen: Sharpen an image by extrapolating from a blurred version.
- Edge detect: Detect edges in an image by convolving it with an edge detection kernel.
- Random dither: Convert an image to a given number of bits per channel, using a randomized threshold.
- Floyd-Steinberg dither: Convert an image to a given number of bits per channel, using dithering with error diffusion.
- Scale: Scale an image up or down in size by a real valued factor.
- Rotate: Rotate an image by a given angle
I tested most image operation with this image of the stone arch bridge:
In the previous homework we implemented the Extract, Crop, and Quantize functions. I started by porting this code to the new project. This was fairly straightforward.
./image -input samples/millcity.jpg -extractChannel 0 -output millcity_extract.jpg
./image -input samples/millcity.jpg -crop 200 200 400 400 -output millcity_crop.jpg
./image -input samples/millcity.jpg -quantize 2 -output millcity_quantize.jpg
Then I tried to tackle some the easier proccessing functions while I learned the structure of the provided code.
./image -input samples/millcity.jpg -noise 0.2 -output millcity_noise.jpg
Contrast and saturation were a little harder…
./image -input samples/millcity.jpg -contrast 100 -output millcity_contrast.jpg
./image -input samples/millcity.jpg -saturation 128 -output millcity_saturation.jpg
The next filters required implementing a convolution kernel.
./image -input samples/millcity.jpg -blur 5 -output millcity_blur.jpg
./image -input samples/millcity.jpg -sharpen 40 -output millcity_sharpen.jpg
./image -input samples/millcity.jpg -blur 5 -edgeDetect -output millcity_edge_detect.jpg
Dithering
./image -input samples/millcity.jpg -randomDither 1 -output millcity_random_dither.jpg
./image -input samples/millcity.jpg -FloydSteinbergDither 1 -output millcity_floyd_dither.jpg
Tranformations
./image -input samples/millcity.jpg -sampling 2 -scale 0.5 0.5 -output millcity_scale.jpg
./image -input samples/millcity.jpg -sampling 1 -rotate 1 -output millcity_rotate.jpg
Sampling
./image -input samples/millcity.jpg -sampling 0 -rotate 0.2 -scale 2.3 3 -output millcity_scale_sample_nn.jpg
./image -input samples/millcity.jpg -sampling 1 -rotate 0.2 -scale 2.3 3 -output millcity_scale_sample_bilinear.jpg
./image -input samples/millcity.jpg -sampling 2 -rotate 0.2 -scale 2.3 3 -output millcity_scale_sample_gaussian.jpg
Art
I really like the rainbow of color on this image. This was generated by accident while trying to implement the convolution kernel.
Art submission
Code: image.cpp
Conclusion
I found this project to be particularily difficult. I ran into a lot of issues where floating points would be implicily cast into ints resulting in some dataloss and artifacting. It took some time to learn the quirks of c++, it’s definately not my favorite language to work with. The dithering implementation was a fun challenge, I enjoyed the combination structured algorithm + programming that it provided.