Programming Workshop 2 (CSCI 1061U)
Winter 2021
Faculty of Science
Ontario Tech University
The goal of this lab is to load, manipulate, and save jpg image files. Instead of writing our own jpeg routines, we will use the single-file public domain libraries for C/C++ that provide basic image read/write functionality. These libraries are available at https://github.com/nothings/stb. You can download these from Github as follows
$ git clone https://github.com/nothings/stb.git
or you can also get it from here.
The following file showcases how you will be able to use these files to read/write a jpeg file.
#include <iostream>
#include <fstream>
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb/stb_image.h"
#include "stb/stb_image_resize.h"
#include "stb/stb_image_write.h"
using namespace std;
int main()
{
int width, height, channels;
unsigned char* img = stbi_load("apple.jpg", &width, &height, &channels, 0);
if (img == 0) {
cout << "Error loading image file" << endl;
return -1;
}
cout << "Loading image\n";
cout << "\twidth = " << width << "\n";
cout << "\theight = " << height << "\n";
cout << "\tchannels = " << channels << "\n";
stbi_write_jpg("apple-copy.jpg", width, height, channels, img, 100);
stbi_image_free(img);
return 0;
}
We can compile this file using
$ g++ main.cpp -o foo
It assumes that STB header files are available in the stb folder as seen below.
$ tree -L 1 .
.
├── apple.jpg
├── bambi.jpg
├── main.cpp
└── stb
├── LICENSE
├── README.md
├── data
├── deprecated
├── docs
├── stb.h
├── stb_c_lexer.h
├── stb_connected_components.h
├── stb_divide.h
├── stb_ds.h
├── stb_dxt.h
├── stb_easy_font.h
├── stb_herringbone_wang_tile.h
├── stb_image.h
├── stb_image_resize.h
├── stb_image_write.h
├── stb_include.h
├── stb_leakcheck.h
├── stb_perlin.h
├── stb_rect_pack.h
├── stb_sprintf.h
├── stb_textedit.h
├── stb_tilemap_editor.h
├── stb_truetype.h
├── stb_vorbis.c
├── stb_voxel_render.h
├── stretchy_buffer.h
├── tests
└── tools
The following lines
int width, height, channels;
unsigned char* img = stbi_load("apple.jpg", &width, &height, &channels, 0 /* desired number of channels = 0 */);
if (img == 0) {
"Error loading image file" << endl;
cout << return -1;
}
load image data int an unsigned char*
buffer. The function also returns the width, height, and the number of channels of an image. Color images are typically stored as RedGreenBlue, so these have 3 channels. The total size of data is width*height*channels
. The function returns a 0 if it is unable to read in the file.
The following lines
"apple-copy.jpg", width, height, channels, img, 100 /* quality = 100 */); stbi_write_jpg(
Save img data to a jpeg file. It requires the width, height, number of channels, plus the buffer containing image data.
Function stbi_load
returns a pointer to an unsigned char*
buffer. The size of this buffer is width * height * channels
. The image data is stored in the buffer in row order, i.e., the first width * channels
bytes belong to the first row of image. The following code sets the first 10 rows of the input image to black.
// Lets set the first 10 rows to black
for (unsigned char* p = img; p != img + 10*width*channels; p += channels) {
uint8_t) 0;
*p = (1) = (uint8_t) 0;
*(p+2) = (uint8_t) 0;
*(p+ }
Image layout is illustrated below.
Complete the following code that creates a noisy image. Specifically, it sets 20% of image pixels to black. The pixels locations are randomly generated.
#include <iostream>
#include <fstream>
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb/stb_image.h"
#include "stb/stb_image_resize.h"
#include "stb/stb_image_write.h"
using namespace std;
void create_a_random_pixel_location(int width, int height, int& r, int &c)
{// TO DO
}
//
// Image structure
//
struct MyImg {
unsigned char* data;
int width;
int channels;
int height;
std::string filename;
};
void print_img_info(MyImg* img)
{"width = " << img->width << "\n"
cout << "height = " << img->height << "\n"
<< "channels = " << img->channels << endl;
<<
}
void delete_img(MyImg** img)
{// Deletes img and sets it to NULL
//
// TO DO
}
//
// Image read/write
//
const string& filename)
MyImg* load_jpeg_file(
{// Returns a MyImg pointer containing image data.
// If the file loading is unsuccessful, it returns a 0
//
// TO DO
return 0;
}
void save_to_jpeg_file(const string& filename, MyImg* img)
{// Saves to a jpeg file
//
// TO DO
}
//
// Setters
//
void set_pixel_red(MyImg* img, int r, int c, uint8_t val)
{// TO DO
}
void set_pixel_green(MyImg* img, int r, int c, uint8_t val)
{// TO DO
}
void set_pixel_blue(MyImg* img, int r, int c, uint8_t val)
{// TO DO
}
//
// Getters
//
uint8_t get_pixel_red(MyImg* img, int r, int c)
{// TO DO
}
uint8_t get_pixel_green(MyImg* img, int r, int c)
{// TO DO
}
uint8_t get_pixel_blue(MyImg* img, int r, int c)
{// TO DO
}
//
// The main function
//
int main()
{"apple.jpg");
MyImg* img = load_jpeg_file(
int total_pixels = img->width * img->height;
// Set 20% of the total_pixels to black
// Save results to apple-noisy.jpg
// Memory cleanup
return 0;
}
The following images show the input image and the noisy image.
Write a function that picks a subregion from the image. The function will have the following signature.
int top, int left, int bottom, int right) MyImg* get_subregion(MyImg* src,
When we use this function as follows
50, 5, 250, 250);
MyImg* img2 = get_subregion(img, "apple-resized.jpg", img2); save_to_jpeg_file(
we get the following
You can allocate a new image of width w
and height h
using malloc
as seen below
unsigned char* data = malloc(w*h*3);
You can still use stbi_image_free(data)
to release the allocated image data.
Write a function that flips an image in place (i.e., the image passed to it is flipped).
void flip(MyImg* img, int dir);
If dir is 1, the image is flipped horizontally (left below), and if it is 0, the image is flipped vertically (right below).
main2.cpp
file, for example, flips the image vertically
..."apple.jpg");
MyImg* img = load_jpeg_file(0);
flip(img, "apple-up-down.jpg", img);
save_to_jpeg_file( ...
Similarly, main3.cpp
file will flip the image horizontally
..."apple.jpg");
MyImg* img = load_jpeg_file(1);
flip(img, "apple-left-right.jpg", img);
save_to_jpeg_file( ...
Think how you would reverse a series of numbers? The following figure illustrates how one might reverse (flip) a series of numbers.
Recall that flipping an image is simply reversing rows (vertical/up-down flip) or reversing columns (horizontal/left-right flip).
You can reuse the get and set pixel methods that you have already developed to complete this task.
Please submit main1.cpp
, main2.cpp
, and main3.cpp
via Canvas.