Give Up Sometimes

The expression “Never, never give up” means to keep trying and never stop working for your goals. Do you agree or disagree with this statement? Use specific reasons and examples to support your answer.

Topic

Many people believe in the saying that we should never give up casually, which tells them to work incessantly for their goals. Admittedly, this spirit-lifting saying has given countless people a pat on the back, inspired them to strive for their long-held dreams and acquire prosperity in the end. But generally speaking, I suppose we don’t have to shy away from giving up. Sometimes we need to put aside our persistence and accept the abnegation.

First of all, the initial orientation matters. We can’t guarantee that the first step we take is towards the right direction, which means we can be heading the wrong without perceiving it. If so, it would be better to just turn around than to press on. There’s no need to sacrifice your success for simply senseless insistence. One example of this is an ancient idiom from China, narrating a man who intended to go south steered his carriage to the north regardless of the prompting from a passer-by. He had not realized the sheer meaninglessness of his action. Of course, he has not arrived his destination yet. Had he listened to others’ advice and turned back, he would have reached it.

Besides, we should pay heed to the progress. Not only are we likely to move forward to where we do not expect at the beginning, but also during the process, and thus adjusting ourselves dynamically should be attached significance to. In addition, even though we know clearly what we are doing and why we are doing it, we may lack the capacity to do it well, and the progress can fall short of our expectation. When we’re patently unsuitable for tackling certain type of things, insisting may not be the optimal option. Some classmates in my high school will be rather representative instances. As soon as they entered the school, each of them picked an Olympiad in a certain subject. Yet before long, they found themselves not very good at competition, thereby opting out, focusing on regular curriculum. It turned out ultimately that they obtained fairly high scores in the college entrance examination. We can derive from it the fact that giving up does not necessarily mean losing, but somehow means wining.

What’s more, the outcome may be perplexing. When it come out, it’s time to judge whether it is right or not. We can scarcely ensure that the result will always fulfil our anticipation, so when we receive an unexpected result which is against the basic facts, probably it should not be calculated in statistics. At this time, alleging that the result has no problem seems a bit stubborn. On the other hand, if the result does not correspond with what we used to deem it should be, the chances are what deceives you is not the result, but the acknowledgement. If we look back upon history of science and technology, there are innumerable examples, such as Galileo who doubted the previous law of free fall, Bohr who suspected the previous nuclear model, etc. All of these indicate that even the most seeming truth has the possibility to be broken someday. The interesting thing is that, the result and the truth were dropped alternately, which set the pace for the development of human civilization. We wouldn’t have today’s advancement if there were no giving up in the world.

The old saying “Never, never give up” will continue to exert its positive attitude and lift people up. However, I take the viewpoint that people sometimes need to give up what they think, have or believe. This is not persuading you to give up everything in your life, but encouraging you to give up in a more sensible way, in which you will better encounter what you love, accomplish what you dream, and make what you desire to be.

Digital Image Processing Assignment I

Main Contents

  • Read a color bmp;
  • RGB->YUV;
  • Color to gray: gray=Y in YUV color space;
  • Rearrange gray intensity to lie between [0,255];
  • Write a grayscale bmp;
  • Change the luminance value Y;
  • YUV->RGB;
  • Write a color bmp.

Step One: BMP File Structure

A common BMP file is comprised of four part: image file header, image information header, palette and image data.

The image file header is a struct whose length is 14 bytes. Here gives its definition,

typedef struct tagBITMAPFILEHEADER {
    WORD bfType;  
    DWORD bfSize;  
    WORD bfReserved1;  
    WORD bfReserved2; 
    DWORD bfOffBits; 
} BITMAPFILEHEADER;

and the explanation for every variable.

  • bfType: must always be set to ‘BM’ to declare that this is a .bmp-file;
  • bfSize: specifies the size of the file in bytes;
  • bfReserved1: must always be set to zero;
  • bfReserved2: must always be set to zero;
  • bfOffBits: specifies the offset from the beginning of the file to the bitmap data.

The image information header is also a struct, while its length is 40 bytes. The definition

typedef struct tagBITMAPINFOHEADER {
    DWORD biSize;     
    LONG biWidth;    
    LONG biHeight;    
    WORD biPlanes;    
    WORD biBitCount   
    DWORD biCompression;  
    DWORD biSizeImage;   
    LONG biXPelsPerMeter;  
    LONG biYPelsPerMeter;  
    DWORD biClrUsed;  
    DWORD biClrImportant; 
} BITMAPINFOHEADER;

The explanation

  • biSize: number of bytes to define BITMAPINFORHEADER structure;
  • biWidth: image width (number of pixels);
  • biHeight: image height (number of pixels), note that if it’s a positive number, the image is inverted, otherwise upright;
  • biPlanes: number of planes, should always be 1;
  • biBitCount: bits per pixel, which may be 1, 4, 8, 16, 24, 32;
  • biCompression: compression type, only non-compression(BI_RGB) is discussed here;
  • biSizeImage: image size with bytes, when biCompression is BI_RGB, biSizeImage is 0;
  • biXPelsPerMeter: horizontal resolution, pixels per meter;
  • biYPelsPerMeter: vertical resolution, pixels per meter;
  • biClrUsed: number of color indices used in the bitmap, when it’s 0, all the palette items are used;
  • biClrImportant: number of important color indices for image display, when it’s 0, all items are important.

The palette has a series of RGBQUADs, which is defined like this.

typedef struct tagRGBQUAD {
    uint8_t rgbBlue;
    uint8_t rgbGreen;
    uint8_t rgbRed;
    uint8_t rgbReserved;
} RGBQUAD;

Note that the order of the color is blue, green, and red, not the reverse. The number of RGBQUADs is decided by biBitCount and biClrUsed.

Next we need to define BITMAPINFO.

typedef struct tagBITMAPINFO {
    BITMAPINFOHEADER bmiHeader;
    RGBQUAD bmiColors[1];
} BITMAPINFO;

As we know nothing about the number of RGBQUADs an image uses, the BITMAPINFO should be defined as a pointer so that right amount of memory can be allocated to it.

The image data contains color of all pixels, and every biBitCount bit(s) represents a pixel.

At last, we define a struct to storage a full BMP image.

typedef struct tagBITMAP {
    BITMAPFILEHEADER bmHeader;
    BITMAPINFO *bmInfo;
    uint32_t bmInfoSize;
    uint32_t bmBytesPerRow;
    uint8_t bmBytesPerPel;
    uint8_t *bmData;
} BITMAP;

When defining the two structs, we need to add a line #pragma pack(push, 1) to avoid struct padding.

Step Two: Read/Write a BMP File

A BMP file is a binary file, so we need to add "b" to the second parameter when using fopen.

Another important thing is that the number of bytes in one row must always be adjusted to fit into the border of a multiple of four, and we need to calculate how many bytes are there in one row.

For convenience, we define an initialize function, which receives bmHeader and bmInfo, and initializes other variables.

// given bmHeader and bmInfo, initialize others
void init_bmp(BITMAP *bmImg) {
    BITMAPINFOHEADER *bmiHeader = &(bmImg->bmInfo->bmiHeader);
    bmImg->bmBytesPerRow = ((bmiHeader->biWidth * bmiHeader->biBitCount + 31) >> 5) << 2;
    bmImg->bmBytesPerPel = bmiHeader->biBitCount >> 3;
    bmImg->bmData = (uint8_t *) malloc(bmImg->bmBytesPerRow * bmiHeader->biHeight);
}

The read function is showed below.

// read a BMP from file
void read_bmp(BITMAP *bmImg, char *filepath) {
    FILE *fiInImg = fopen(filepath, "rb");
    BITMAPINFOHEADER bmiHeader;
    fread(&(bmImg->bmHeader), sizeof(BITMAPFILEHEADER), 1, fiInImg);
    fread(&bmiHeader, sizeof(BITMAPINFOHEADER), 1, fiInImg);
    // if biBitCount is less than 16, use all the palette, otherwise do not use palette
    if (bmiHeader.biBitCount < 16) {
        bmImg->bmInfoSize = sizeof(BITMAPINFOHEADER) + (sizeof(RGBQUAD) << bmiHeader.biBitCount);
    } else {
        bmImg->bmInfoSize = sizeof(BITMAPINFOHEADER);
    }
    bmImg->bmInfo = (BITMAPINFO *) malloc(bmImg->bmInfoSize);
    bmImg->bmInfo->bmiHeader = bmiHeader;
    if (bmiHeader.biBitCount < 16) {
        fread(bmImg->bmInfo->bmiColors, sizeof(RGBQUAD), 1 << bmiHeader.biBitCount, fiInImg);
    }
    init_bmp(bmImg);
    fread(bmImg->bmData, bmImg->bmBytesPerRow, bmiHeader.biHeight, fiInImg);
    fclose(fiInImg);
}

The write function looks similar with the read function.

// write a BMP to file
void write_bmp(BITMAP *bmImg, char *filepath) {
    FILE *fiOutImg = fopen(filepath, "wb");
    fwrite(&(bmImg->bmHeader), sizeof(BITMAPFILEHEADER), 1, fiOutImg);
    fwrite(bmImg->bmInfo, bmImg->bmInfoSize, 1, fiOutImg);
    fwrite(bmImg->bmData, bmImg->bmBytesPerRow, bmImg->bmInfo->bmiHeader.biHeight, fiOutImg);
    fclose(fiOutImg);
}

Step Three: Change Color to Gray

To make things easier, we define a function to duplicate a BMP file,

// duplicate a BMP
void copy_bmp(BITMAP *bmDes, BITMAP *bmSrc) {
    memcpy(bmDes, bmSrc, sizeof(BITMAP));
    bmDes->bmInfo = (BITMAPINFO *) malloc(bmSrc->bmInfoSize);
    memcpy(bmDes->bmInfo, bmSrc->bmInfo, bmSrc->bmInfoSize);
    BITMAPINFOHEADER *bmiHeader = &(bmSrc->bmInfo->bmiHeader);
    bmDes->bmData = (uint8_t *) malloc(bmSrc->bmBytesPerRow * bmiHeader->biHeight);
    memcpy(bmDes->bmData, bmSrc->bmData, bmSrc->bmBytesPerRow * bmiHeader->biHeight);
}

and a function to make sure the RGB value of a pixel lie between [0, 255].

// make RGB value legal
uint8_t adjust(double val) {
    int16_t ret = (int16_t) (val + 0.5);
    return ret < 0 ? 0 : ret > 255 ? 255 : ret;
}

We need to change RGB to YUV first, as the grayscale is determined by Y value. The formula is

$$\begin{bmatrix} 0.299 & 0.587 & 0.114 \\ -0.147 & -0.289 & 0.436 \\ 0.615 & -0.515 & -0.100 \end{bmatrix} \times \begin{bmatrix} R \\ G \\ B \end{bmatrix} = \begin{bmatrix} Y \\ U \\ V \end{bmatrix}$$

    // calculate YUV value
    double *bmYUV = (double *) malloc(sizeof(double) * bmImg.bmBytesPerRow * bmiHeader->biHeight);
    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
            uint32_t pos = h * bmImg.bmBytesPerRow + w * bmImg.bmBytesPerPel;
            uint8_t *B = &bmImg.bmData[pos];
            uint8_t *G = &bmImg.bmData[pos + 1];
            uint8_t *R = &bmImg.bmData[pos + 2];
            bmYUV[pos] = 0.299 * *R + 0.587 * *G + 0.114 * *B;
            bmYUV[pos + 1] = -0.147 * *R - 0.289 * *G + 0.436 * *B;
            bmYUV[pos + 2] = 0.615 * *R - 0.515 * *G - 0.100 * *B;
        }
    }

To change color to gray, we can simply make the R, G and B value of a pixel equal to its Y value. It would be more complex if we want to change it into an image with biBitCount equal to 8, because we need to set the palette manually.

One more step, rearrange gray intensity to lie between [0,255]. It’s just a math problem. So the code

    // color to gray
    BITMAP bmGray;
    bmGray.bmHeader = bmImg.bmHeader;
    bmGray.bmInfoSize = sizeof(BITMAPINFOHEADER) + (sizeof(RGBQUAD) << 8);
    bmGray.bmInfo = (BITMAPINFO *) malloc(bmGray.bmInfoSize);
    bmGray.bmInfo->bmiHeader = *bmiHeader;
    bmGray.bmInfo->bmiHeader.biBitCount = 8;
    for (int i = 0; i < 256; ++i) {
        RGBQUAD *rgb = &(bmGray.bmInfo->bmiColors[i]);
        rgb->rgbBlue = i;
        rgb->rgbGreen = i;
        rgb->rgbRed = i;
        rgb->rgbReserved = 0;
    }
    init_bmp(&bmGray);
    uint8_t min = 255, max = 0;
    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
            uint32_t pos = h * bmImg.bmBytesPerRow + w * bmImg.bmBytesPerPel;
            double *Y = &bmYUV[pos];
            if (*Y < min) {
                min = *Y;
            }
            if (*Y > max) {
                max = *Y;
            }
        }
    }
    // rearrange gray indensity
    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
            uint32_t pos = h * bmImg.bmBytesPerRow + w * bmImg.bmBytesPerPel;
            double *Y = &bmYUV[pos];
            uint32_t _pos = h * bmGray.bmBytesPerRow + w * bmGray.bmBytesPerPel;
            bmGray.bmData[_pos] = adjust(255 * (*Y - min) / (max - min));
        }
    }

Step Four: Change the Luminance

The luminance is depend on Y value, too. What we need to do is just changing the Y value and applying the inverse formula below.

$$\begin{bmatrix} 1.000 & 0.000 & 1.140 \\ 1.000 & -0.3946 & -0.5805 \\ 1.000 & 2.032 & -0.0005 \end{bmatrix} \times \begin{bmatrix} Y \\ U \\ V \end{bmatrix} = \begin{bmatrix} R \\ G \\ B \end{bmatrix}$$

And the code is simple.

    // change luminance
    BITMAP bmLight, bmDark;
    copy_bmp(&bmLight, &bmImg);
    copy_bmp(&bmDark, &bmImg);
    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
            uint32_t pos = h * bmImg.bmBytesPerRow + w * bmImg.bmBytesPerPel;
            double *Y = &bmYUV[pos];
            double *U = &bmYUV[pos + 1];
            double *V = &bmYUV[pos + 2];
            bmLight.bmData[pos] = adjust(*Y + 25 + 2.032 * *U - 0.0005 * *V);
            bmLight.bmData[pos + 1] = adjust(*Y + 25 - 0.3946 * *U - 0.5805 * *V);
            bmLight.bmData[pos + 2] = adjust(*Y + 25 + 1.140 * *V);
            bmDark.bmData[pos] = adjust(*Y - 50 + 2.032 * *U - 0.0005 * *V);
            bmDark.bmData[pos + 1] = adjust(*Y - 50 - 0.3946 * *U - 0.5805 * *V);
            bmDark.bmData[pos + 2] = adjust(*Y - 50 + 1.140 * *V);
        }
    }

Step Five: Complete Source Code

bmp.h

#ifndef _BMP_H_
#define _BMP_H_

#include <stdint.h>

#pragma pack(push, 1) // avoid struct padding

typedef struct tagBITMAPFILEHEADER {
    uint16_t bfType;
    uint32_t bfSize;
    uint16_t bfReserved1;
    uint16_t bfReserved2;
    uint32_t bfOffBits;
} BITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER {
    uint32_t biSize;
    int32_t biWidth;
    int32_t biHeight;
    uint16_t biPlanes;
    uint16_t biBitCount;
    uint32_t biCompression;
    uint32_t biSizeImage;
    int32_t biXPelsPerMeter;
    int32_t biYPelsPerMeter;
    uint32_t biClrUsed;
    uint32_t biClrImportant;
} BITMAPINFOHEADER;

typedef struct tagRGBQUAD {
    uint8_t rgbBlue;
    uint8_t rgbGreen;
    uint8_t rgbRed;
    uint8_t rgbReserved;
} RGBQUAD;

typedef struct tagBITMAPINFO {
    BITMAPINFOHEADER bmiHeader;
    RGBQUAD bmiColors[1];
} BITMAPINFO;

typedef struct tagBITMAP {
    BITMAPFILEHEADER bmHeader;
    BITMAPINFO *bmInfo;
    uint32_t bmInfoSize;
    uint32_t bmBytesPerRow;
    uint8_t bmBytesPerPel;
    uint8_t *bmData;
} BITMAP;

#pragma pack(pop)

#endif

bmp.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bmp.h"

// given bmHeader and bmInfo, initialize others
void init_bmp(BITMAP *bmImg) {
    BITMAPINFOHEADER *bmiHeader = &(bmImg->bmInfo->bmiHeader);
    bmImg->bmBytesPerRow = ((bmiHeader->biWidth * bmiHeader->biBitCount + 31) >> 5) << 2;
    bmImg->bmBytesPerPel = bmiHeader->biBitCount >> 3;
    bmImg->bmData = (uint8_t *) malloc(bmImg->bmBytesPerRow * bmiHeader->biHeight);
}

// read a BMP from file
void read_bmp(BITMAP *bmImg, char *filepath) {
    FILE *fiInImg = fopen(filepath, "rb");
    BITMAPINFOHEADER bmiHeader;
    fread(&(bmImg->bmHeader), sizeof(BITMAPFILEHEADER), 1, fiInImg);
    fread(&bmiHeader, sizeof(BITMAPINFOHEADER), 1, fiInImg);
    // if biBitCount is less than 16, use all the palette, otherwise do not use palette
    if (bmiHeader.biBitCount < 16) {
        bmImg->bmInfoSize = sizeof(BITMAPINFOHEADER) + (sizeof(RGBQUAD) << bmiHeader.biBitCount);
    } else {
        bmImg->bmInfoSize = sizeof(BITMAPINFOHEADER);
    }
    bmImg->bmInfo = (BITMAPINFO *) malloc(bmImg->bmInfoSize);
    bmImg->bmInfo->bmiHeader = bmiHeader;
    if (bmiHeader.biBitCount < 16) {
        fread(bmImg->bmInfo->bmiColors, sizeof(RGBQUAD), 1 << bmiHeader.biBitCount, fiInImg);
    }
    init_bmp(bmImg);
    fread(bmImg->bmData, bmImg->bmBytesPerRow, bmiHeader.biHeight, fiInImg);
    fclose(fiInImg);
}

// duplicate a BMP
void copy_bmp(BITMAP *bmDes, BITMAP *bmSrc) {
    memcpy(bmDes, bmSrc, sizeof(BITMAP));
    bmDes->bmInfo = (BITMAPINFO *) malloc(bmSrc->bmInfoSize);
    memcpy(bmDes->bmInfo, bmSrc->bmInfo, bmSrc->bmInfoSize);
    BITMAPINFOHEADER *bmiHeader = &(bmSrc->bmInfo->bmiHeader);
    bmDes->bmData = (uint8_t *) malloc(bmSrc->bmBytesPerRow * bmiHeader->biHeight);
    memcpy(bmDes->bmData, bmSrc->bmData, bmSrc->bmBytesPerRow * bmiHeader->biHeight);
}

// write a BMP to file
void write_bmp(BITMAP *bmImg, char *filepath) {
    FILE *fiOutImg = fopen(filepath, "wb");
    fwrite(&(bmImg->bmHeader), sizeof(BITMAPFILEHEADER), 1, fiOutImg);
    fwrite(bmImg->bmInfo, bmImg->bmInfoSize, 1, fiOutImg);
    fwrite(bmImg->bmData, bmImg->bmBytesPerRow, bmImg->bmInfo->bmiHeader.biHeight, fiOutImg);
    fclose(fiOutImg);
}

// make RGB value legal
uint8_t adjust(double val) {
    int16_t ret = (int16_t) (val + 0.5);
    return ret < 0 ? 0 : ret > 255 ? 255 : ret;
}

int main(int argc, char *argv[]) {
    // read bmp file
    BITMAP bmImg;
    read_bmp(&bmImg, "original.bmp");
    BITMAPINFOHEADER *bmiHeader = &(bmImg.bmInfo->bmiHeader);

    // calculate YUV value
    double *bmYUV = (double *) malloc(sizeof(double) * bmImg.bmBytesPerRow * bmiHeader->biHeight);
    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
            uint32_t pos = h * bmImg.bmBytesPerRow + w * bmImg.bmBytesPerPel;
            uint8_t *B = &bmImg.bmData[pos];
            uint8_t *G = &bmImg.bmData[pos + 1];
            uint8_t *R = &bmImg.bmData[pos + 2];
            bmYUV[pos] = 0.299 * *R + 0.587 * *G + 0.114 * *B;
            bmYUV[pos + 1] = -0.147 * *R - 0.289 * *G + 0.436 * *B;
            bmYUV[pos + 2] = 0.615 * *R - 0.515 * *G - 0.100 * *B;
        }
    }

    // color to gray
    BITMAP bmGray;
    bmGray.bmHeader = bmImg.bmHeader;
    bmGray.bmInfoSize = sizeof(BITMAPINFOHEADER) + (sizeof(RGBQUAD) << 8);
    bmGray.bmInfo = (BITMAPINFO *) malloc(bmGray.bmInfoSize);
    bmGray.bmInfo->bmiHeader = *bmiHeader;
    bmGray.bmInfo->bmiHeader.biBitCount = 8;
    for (int i = 0; i < 256; ++i) {
        RGBQUAD *rgb = &(bmGray.bmInfo->bmiColors[i]);
        // to prove that the palette works well, play a small trick
        rgb->rgbBlue = (i >> 4) << 4;
        rgb->rgbGreen = (i >> 4) << 4;
        rgb->rgbRed = (i >> 4) << 4;
        rgb->rgbReserved = 0;
    }
    init_bmp(&bmGray);
    uint8_t min = 255, max = 0;
    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
            uint32_t pos = h * bmImg.bmBytesPerRow + w * bmImg.bmBytesPerPel;
            double *Y = &bmYUV[pos];
            if (*Y < min) {
                min = *Y;
            }
            if (*Y > max) {
                max = *Y;
            }
        }
    }
    // rearrange gray indensity
    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
            uint32_t pos = h * bmImg.bmBytesPerRow + w * bmImg.bmBytesPerPel;
            double *Y = &bmYUV[pos];
            uint32_t _pos = h * bmGray.bmBytesPerRow + w * bmGray.bmBytesPerPel;
            bmGray.bmData[_pos] = adjust(255 * (*Y - min) / (max - min));
        }
    }
    write_bmp(&bmGray, "gray.bmp");

    // change luminance
    BITMAP bmLight, bmDark;
    copy_bmp(&bmLight, &bmImg);
    copy_bmp(&bmDark, &bmImg);
    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
            uint32_t pos = h * bmImg.bmBytesPerRow + w * bmImg.bmBytesPerPel;
            double *Y = &bmYUV[pos];
            double *U = &bmYUV[pos + 1];
            double *V = &bmYUV[pos + 2];
            bmLight.bmData[pos] = adjust(*Y + 25 + 2.032 * *U - 0.0005 * *V);
            bmLight.bmData[pos + 1] = adjust(*Y + 25 - 0.3946 * *U - 0.5805 * *V);
            bmLight.bmData[pos + 2] = adjust(*Y + 25 + 1.140 * *V);
            bmDark.bmData[pos] = adjust(*Y - 50 + 2.032 * *U - 0.0005 * *V);
            bmDark.bmData[pos + 1] = adjust(*Y - 50 - 0.3946 * *U - 0.5805 * *V);
            bmDark.bmData[pos + 2] = adjust(*Y - 50 + 1.140 * *V);
        }
    }
    write_bmp(&bmLight, "light.bmp");
    write_bmp(&bmDark, "dark.bmp");

    return 0;
}