User Tools

Site Tools


cs19s23as08

cs19s23as08: Bitmap Image Modification

Goals

  • Practice working with explicitly binary data in C++.
  • Learn a bit about one of the simplest image file formats.

Prerequisites

This assignment requires familiarity with the lecture materials presented in class through week 08.


Background: The BMP File Format

The BMP file format is among the simplest image representation formats; possibly the simplest. There are various versions of the format, and the format is able to support several kinds of color values and compression. But the simplest and most common bitmap files are not compressed.

For our purposes, assume that a BMP file consists of 24-bit RGB pixels and is composed of the following “structures” (i.e., segments of data) in the following order:

Structure name Size Contents
BMP file header 14 bytes General information about the file
DIB header Varies Information about the image, e.g. width and height
Pixel array Based on image dimensions and pixel type Component values for each pixel

The Wikipedia article linked above has a lot more information on the format. However, here is a thorough description of what you need to know about each structure for this assignment:

BMP File Header

This structure consists of 14 bytes, as follows:

File offset Size (bytes) Purpose
0x00 2 The bytes 0x42 and 0x4d (characters 'B' and 'M') identify this file as a bitmap image
0x02 4 The size of the BMP file in bytes (should match the actual file size)
0x06 2 Reserved; actual value depends on the application that creates the image
0x08 2 Reserved; actual value depends on the application that creates the image
0x0a 4 The offset, i.e. starting address, of the byte where the bitmap image data (pixel array) can be found.

DIB Header

This structure immediately follows the BMP header and consists of a variable number of bytes, as follows:

File Offset Size (bytes) Description
0x0e 4 The size of this header.
0x12 4 The bitmap width in pixels (signed 32-bit integer)
0x16 4 The bitmap height in pixels (signed 32-bit integer)
0x1a 2 The number of color planes (must be 1)
0x1c 2 The number of bits per pixel. In our case, this should be 24.
0x1e 4 The compression method being used. In our case, this should be 0.
0x22 4 The image size. This is the size of the raw bitmap data (pixel array).
0x26 4 The horizontal resolution of the image (pixels per meter), as a signed 32-bit integer.
0x2a 4 The vertical resolution of the image (pixels per meter), as a signed 32-bit integer.
0x2e 4 The number of colors in the color palette. In our case, this should be 0.
0x32 4 The number of important colors used, or 0 when every color is important. In our case, this should be 0.

Pixel Array

This structure contains component values for every pixel in the image.

  1. The pixel array begins at the file offset specified in the BMP file header.
  2. Each pixel value is composed of 3 bytes, which represent a 24-bit RGB color, i.e. 3 8-bit unsigned integers.
  3. Pixel values are presented row-by-row starting with the bottom row1), i.e. the entire bottom row of pixels, then the row second from the bottom, etc.
  4. Each row is padded to a multiple of 4 bytes in size, if necessary.

Example

A very small (2×2-pixel) BMP file is available at /srv/datasets/simple.bmp, or via HTTP. (This is the same image described in Example 1 of Wikipedia's article.)

Here it is, surrounded by a 10-pixel padded bordering box (you probably can't see the image very easily!): <html><img src=“https://jeff.cis.cabrillo.edu/datasets/simple.bmp” style=“padding: 10px; border: 1px solid black;”></html>

And here is how it would look if it were resized to 200×200 pixels: <html><img src=“https://jeff.cis.cabrillo.edu/datasets/simple.bmp.200x200.png” style=“border: 1px solid black;”></html>

On the command line, we could inspect the contents of this file as 1-byte hexadecimal values using the hexdump utility:

hexdump -C /srv/datasets/simple.bmp

hexdump gives the following output, in which each line displays the file offset in hexadecimal, followed by sixteen space-separated, two column, hexadecimal bytes, followed by the same sixteen bytes presented as printable characters (or periods if not printable), enclosed in | characters.:

00000000  42 4d 46 00 00 00 00 00  00 00 36 00 00 00 28 00  |BMF.......6...(.|
00000010  00 00 02 00 00 00 02 00  00 00 01 00 18 00 00 00  |................|
00000020  00 00 10 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000030  00 00 00 00 00 00 00 00  ff ff ff ff 00 00 ff 00  |................|
00000040  00 00 ff 00 00 00                                 |......|
00000046

The first 14 bytes constitute the BMP File Header: 42 4d 46 00 00 00 00 00 00 00 36 00 00 00

Note the bytes 42 4d initially, i.e. the characters B and M. The next 4 bytes (46 00 00 00) are the file size. While this looks to be (and is) the value 0x46000000, note that this does not mean the file is size 1174405120, which is how we would normally interpret 0x46000000. This is because BMP files (and most modern CPUs) represent integers in "little-endian" format, meaning that the bytes are ordered from least significant to most significant, unlike how we usually write integers for human benefit. This means the way we would normally write that value in hexadecimal is 0x00000046, which is 70 in decimal. The size of this file is indeed 70 bytes:

$ stat -c '%s' /srv/datasets/simple.bmp
70

The next 40 bytes constitute the DIB Header: 28 00 00 00 02 00 00 00 02 00 00 00 01 00 18 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Note that 0x28000000 is the header size (little-endian 40), and the next 8 bytes are the image width and height, both 2 in this case: 0x02000000 0x02000000.

From offset 0x36 to the end of the file is the pixel array: 00 00 ff ff ff ff 00 00 ff 00 00 00 ff 00 00 00

The first row of pixels: 00 00 ff ff ff ff 00 00

The second row of pixels: ff 00 00 00 ff 00 00 00

Note that first-row pixel values 00 00 ff and ff ff ff are the colors red (255, 0, 0) and white (255, 255, 255), respectively, and that even pixel values are stored as little-endian integers, i.e. BGR instead of RGB.


Assignment

You shall write a C++ program that applies one of six different modifications to an existing 24-bit RGB BMP image file and produces another BMP image file containing the result. The resulting image shall only differ in pixel values, i.e. all header and other metadata shall pass through unchanged. The general syntax shall be:

executable input_file output_file modification

The six available modifications are described below, using sample executable cs19_bmp_mod (which is available on the server) as an example, and file /srv/datasets/cabrillo-logo.bmp as input.

Perform these modifications by working with the raw contents of the file. Use only standard C++ and the C++ standard library. Do not use any image-specific third-party code!

Modification -brighten

-brighten increases the intensity of each pixel component by a factor given as a subsequent command-line argument, e.g in the following that increases the intensity by 25%:

cs19_bmp_mod /srv/datasets/cabrillo-logo.bmp cabrillo-logo-brighten-25.bmp -brighten 25

Original image:

Cabrillo College logo

Modified image:

Cabrillo College logo with pixel components brightened by 25%

Modification -darken

-darken decreases the intensity of each pixel component by a factor given as a subsequent command-line argument, e.g in the following that decreases the intensity by 25%:

cs19_bmp_mod /srv/datasets/cabrillo-logo.bmp cabrillo-logo-darken-25.bmp -darken 25

Original image:

Cabrillo College logo

Modified image:

Cabrillo College logo with pixel components darkened by 25%

Modification -desaturate

-desaturate converts all colors in the image to corresponding shades of gray, where each component in each pixel is assigned the average of all of the pixel's original components, e.g in the following:

cs19_bmp_mod /srv/datasets/cabrillo-logo.bmp cabrillo-logo-desaturate.bmp -desaturate

Original image:

Cabrillo College logo

Modified image:

Cabrillo College logo with pixel components desaturated via the average of components

Modification -invert

-invert inverts all pixel components, e.g in the following:

cs19_bmp_mod /srv/datasets/cabrillo-logo.bmp cabrillo-logo-invert.bmp -invert

Original image:

Cabrillo College logo

Modified image:

Cabrillo College logo with pixel components inverted

Modification -hflip

-hflip flips the image horizontally.

cs19_bmp_mod /srv/datasets/cabrillo-logo.bmp cabrillo-logo-hflip.bmp -hflip

Original image:

Cabrillo College logo

Modified image:

Cabrillo College logo flipped horizontally

Modification -vflip

-vflip flips the image vertically.

cs19_bmp_mod /srv/datasets/cabrillo-logo.bmp cabrillo-logo-vflip.bmp -vflip

Original image:

Cabrillo College logo

Modified image:

Cabrillo College logo flipped vertically

Leaderboard

As submissions are received, this leaderboard will be updated with the top-performing fully/nearly functional solutions, with regard to execution speed.

<html>

<table> <thead> <tr><th>Rank</th><th>Test Time (s)</th><th>Memory Usage (kB)</th><th>SLOC (lines)</th><th>User</th></tr> </thead> <tbody id=“leaderboard-table”> </tbody> </table> <script> function updateLeaderboard() { window.fetch(`/~turnin/leaderboard-cs19s23as08.txt?t=${new Date().getTime()}`, {

method: 'get'

}).then(response ⇒

response.text()

).then(text ⇒ {

let updated = document.getElementById('leaderboard-updated');
updated.innerText = `(Last updated: ${new Date()})`;
let lines = text.split('\n');
let table = document.getElementById('leaderboard-table');
while (table.firstChild)
  table.removeChild(table.firstChild);
for (let i = 0; i < lines.length; ++i) {
  let tokens = lines[i].split(' ').filter(token => token.length > 0);
  if (tokens.length < 2)
    continue;
  let tdRank = document.createElement('td');
  tdRank.textContent = i + 1;
  let tdTime = document.createElement('td');
  tdTime.textContent = Number(tokens[0]).toFixed(4);
  let tdMemUsage = document.createElement('td');
  tdMemUsage.textContent = tokens[1];
  let tdSloc = document.createElement('td');
  tdSloc.textContent = tokens[2];
  let tdUser = document.createElement('td');
  let userLink = document.createElement('a');
  userLink.href = `/~${tokens[3]}/`;
  userLink.target = '_blank';
  userLink.textContent = tokens[3];
  tdUser.appendChild(userLink);
  let tr = document.createElement('tr');
  tr.appendChild(tdRank);
  tr.appendChild(tdTime);
  tr.appendChild(tdMemUsage);
  tr.appendChild(tdSloc);
  tr.appendChild(tdUser);
  table.appendChild(tr);
}

}).catch(err ⇒ {

console.log('Something bad happened: ' + err);

});

window.setTimeout(updateLeaderboard, 60000);

} updateLeaderboard(); </script> </html>


Submission

Submit your source-code file(s) via turnin.

Feedback Robot

This project has a feedback robot that will run some tests on your submission and provide you with a feedback report via email within roughly one minute.

Please read the feedback carefully.

Due Date and Point Value

Due at 23:59:59 on the date listed on the syllabus.

Assignment 08 is worth 60 points of extra credit!

Possible point values per category:
---------------------------------------
Modifications (must work perfectly):
  -brighten                          10
  -darken                            10
  -desaturate                        10
  -invert                            10
  -hflip                             10
  -vflip                             10
Possible deductions:
  Style and practices            10–20%
---------------------------------------
1)
This is due to some troublesome mathematicians, as is often the case 😉.
cs19s23as08.txt · Last modified: 2023-03-27 17:22 by 127.0.0.1