hye,
anybody know how to convert 8bit grayscale image to halftone or dither?
Would you share the source code for that? :)

Recommended Answers

All 4 Replies

I have only dealt with bitmaps (.bmp), but essentially you need to open the file, read in the header information (stuff about how big the file is, width, height, # of colours, etc.) followed by the image data.

I'm assuming by dither or halftone, you mean to cut down the number of colours (like to black and white). If so, you would loop through the image data and change each pixel depending on the RGB value already there. If you are aiming to reduce the size of the image, you may need to process the pixels a bit differently and you will need to mess with the header.

The BMP file format can be found here: http://www.fortunecity.com/skyscraper/windows/364/bmpffrmt.html

could you show me some example of the source code? coz i'm still blur.

Thank you for reply..

Here is how I load a bitmap in my bitmap class (note that my bitmap class only supports 24bit bitmaps):

// Loads bitmap from a file, converts to grayscale
bool Bitmap::load_bitmap_file( char *filename ) {
    ifstream fileStream;
    int fileLength;
    bool previouslyLoaded = this->loaded;
    
    this->loaded = false;
    
    //open filename in read binary mode
    fileStream.open( filename, ifstream::binary );
    if( !fileStream.is_open() )
        return false;
    
    // get length of file:
    fileStream.seekg (0, ios::end);
    fileLength = fileStream.tellg();
    fileStream.seekg (0, ios::beg);
  
    //read the bitmap file header
    fileStream.read( (char*)&( (this->bitmapFileHeader).bfType ), sizeof( WORD ) );
    fileStream.read( (char*)&( (this->bitmapFileHeader).bfSize ), sizeof( DWORD ) );
    fileStream.read( (char*)&( (this->bitmapFileHeader).bfReserved1 ), sizeof( WORD ) );
    fileStream.read( (char*)&( (this->bitmapFileHeader).bfReserved2 ), sizeof( WORD ) );
    fileStream.read( (char*)&( (this->bitmapFileHeader).bfOffBits ), sizeof( DWORD ) );
    
    //verify that this is a bmp file by check bitmap id
    if( (this->bitmapFileHeader).bfType != 0x4D42 ) {
        fileStream.close();
        return false;
    }
    
    //read the bitmap info header
    fileStream.read( (char*)&( (this->bitmapInfoHeader).biSize ), sizeof( DWORD ) );
    fileStream.read( (char*)&( (this->bitmapInfoHeader).biWidth ), sizeof( DWORD ) );
    fileStream.read( (char*)&( (this->bitmapInfoHeader).biHeight ), sizeof( DWORD ) );
    fileStream.read( (char*)&( (this->bitmapInfoHeader).biPlanes ), sizeof( WORD ) );
    fileStream.read( (char*)&( (this->bitmapInfoHeader).biBitCount ), sizeof( WORD ) );
    fileStream.read( (char*)&( (this->bitmapInfoHeader).biCompression ), sizeof( DWORD ) );
    fileStream.read( (char*)&( (this->bitmapInfoHeader).biSizeImage ), sizeof( DWORD ) );
    fileStream.read( (char*)&( (this->bitmapInfoHeader).biXPelsPerMeter ), sizeof( DWORD ) );
    fileStream.read( (char*)&( (this->bitmapInfoHeader).biYPelsPerMeter ), sizeof( DWORD ) );
    fileStream.read( (char*)&( (this->bitmapInfoHeader).biClrUsed ), sizeof( DWORD ) );
    fileStream.read( (char*)&( (this->bitmapInfoHeader).biClrImportant ), sizeof( DWORD ) );
    
    //move file point to the begging of bitmap data
    fileStream.seekg( (this->bitmapFileHeader).bfOffBits );
    
    //allocate enough memory for the bitmap image data
    if( previouslyLoaded ) {
        delete[] this->bitmapImage;
    }
    this->bitmapImage = new unsigned char[ (this->bitmapInfoHeader).biHeight * (this->bitmapInfoHeader).biWidth * 3 ];
    
    //verify memory allocation
    if( bitmapImage == NULL ) {
        fileStream.close();
        return false;
    }
    
    //read in the bitmap image data
    unsigned char junk[4];
    for( int i = 0; i < (this->bitmapInfoHeader).biHeight; i++ ) {
        fileStream.read( (char*) &(this->bitmapImage[i*(this->bitmapInfoHeader).biWidth * 3]), (this->bitmapInfoHeader).biWidth * 3 );
        // Discard padding bytes
        if( (this->bitmapInfoHeader).biWidth % 4 != 0 ) {
            fileStream.read( (char*)junk, (this->bitmapInfoHeader).biWidth % 4 );
        }
    }
    
    
    //make sure bitmap image data was read
    if( bitmapImage == NULL ) {
        fileStream.close();
        return false;
    }
    
    // Bitmap is BGR
    for( int imageIdx = 0; imageIdx < (this->bitmapInfoHeader).biHeight * (this->bitmapInfoHeader).biWidth * 3; imageIdx+=3 ) {
        unsigned char temp = this->bitmapImage[imageIdx];
        this->bitmapImage[imageIdx] = this->bitmapImage[imageIdx + 2];
        this->bitmapImage[imageIdx + 2] = temp;
    }
    
    // Copy filename
    if( previouslyLoaded ) {
        delete[] this->filename;
    }
    this->filename = new char[ strlen(filename) + 1 ];
    strcpy( this->filename, filename );
    
    //close file
    fileStream.close();
    this->loaded = true;
    
    this->print_info();
    
    return true;
}

Here is an example of how to alter the image data:

/**
 * Converts entire image to monochrome (black/white).
**/
bool Bitmap::convert_to_monochrome( int threshold ) {
    for( int i = 0; i < (this->bitmapInfoHeader).biHeight * (this->bitmapInfoHeader).biWidth * 3; i++ ) {
        if( this->bitmapImage[i] > threshold ) {
            this->bitmapImage[i] = 255;
        }
        else {
            this->bitmapImage[i] = 0;    
        }
    }
    return true;
}

Note that the above method already assumes we have a grayscale image (i was only working with grayscale). Grayscale images always have R, G, and B values that are equal. The threshold is the number at which we separate black from white, so commonly you would use 127 in this case (0-127 = black, 128-255 = white).

wow! amazing..
nice explanation.. i'll try this out..
thank you for helping me..

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.