I have written a primitive Bitmap Writer, that prepares a bitmap header, calculates a data section and write it as binary file.
The problem is, the program working for 800x600 resolution however, not working for some arbitrary resolution.
Bitmap Writer Code
#include <stdio.h>
#include <stdlib.h> // malloc()
#include <string.h>
/*
* File Write , check this link : https://www.codingunit.com/c-tutorial-binary-file-io
*
*/
typedef enum { FALSE, TRUE } boolean;
/* For 54 byte header data
*
* http://blog.paphus.com/blog/2012/08/14/write-your-own-bitmaps/
*/
#pragma pack(push,1)
typedef struct BitmapHeaderType {
unsigned char b0; // 0
unsigned char b1; // 1
unsigned int fullFileSize; // 2...5
unsigned int reserved; // 6...9
unsigned int pixelOffset; // 10..13 HEADER SIZE
unsigned int bitmapInfoHeaderSize; // 14..17 BITMAP INFO HEADER SIZE
unsigned int pixelWidth; // 18..21 image width
unsigned int pixelHeight; // 22..25 image height
unsigned short int numberOfColorPlanes; // 26..27 the number of color planes
unsigned short int bitPerPixel; // 28..29 24bit, 32bit, bit size
unsigned int compessionState; // 30..33 compression state, 0 for disable compression
unsigned int sizeOfRawPixel; // 34..37 size of pixel data including paddings (24 bit padding changes data section)
unsigned int horizontalResolution; // 38..41 just leave 2835
unsigned int verticalResolution; // 42..45 just leave 2835
unsigned int numberOfColors; // 46..49 set 0 for default
unsigned int numberOfImportantColors; // 50..53 set 0 for default
} BitmapHeader;
#pragma pack(pop)
void handleBitmapHeader(BitmapHeader *header, int width, int height);
int closestMultipleOfFour(int num);
void writeToFile(unsigned char* bytes, int len, char fileName[]);
void setPixel(unsigned char *data, int x, int y, unsigned char red, unsigned char green, unsigned char blue, int width, int height);
int main()
{
//int width = 800; // compiling OK, runtime OK
int width = 100; // compiling OK, runtime FAILED
int height = 600;
BitmapHeader *header = (BitmapHeader *)malloc(sizeof(BitmapHeader));
handleBitmapHeader(header, width, height);
unsigned char *data = (unsigned char *)malloc(header->sizeOfRawPixel);
unsigned char *bitmap = (unsigned char *)malloc(header->fullFileSize);
// left top corner
setPixel(data, 0 , 0 , 255, 0, 0, width, height);
setPixel(data, 0 , 1 , 255, 0, 0, width, height);
setPixel(data, 1 , 0 , 255, 0, 0, width, height);
setPixel(data, 1 , 1 , 255, 0, 0, width, height);
// right top corner
setPixel(data, width-1 , 0 , 255, 0, 0, width, height);
setPixel(data, width-1 , 1 , 255, 0, 0, width, height);
setPixel(data, width-2 , 0 , 255, 0, 0, width, height);
setPixel(data, width-2 , 1 , 255, 0, 0, width, height);
// left bottom corner
setPixel(data, 0 , height-1 , 255, 0, 0, width, height);
setPixel(data, 0 , height-2 , 255, 0, 0, width, height);
setPixel(data, 1 , height-1 , 255, 0, 0, width, height);
setPixel(data, 1 , height-2 , 255, 0, 0, width, height);
// right bottom corner
setPixel(data, width-1 , height-1 , 255, 0, 0, width, height);
setPixel(data, width-1 , height-2 , 255, 0, 0, width, height);
setPixel(data, width-2 , height-1 , 255, 0, 0, width, height);
setPixel(data, width-2 , height-2 , 255, 0, 0, width, height);
// copy header to bitmap
memcpy(bitmap, header, header->pixelOffset);
// copy data to bitmap
memcpy(bitmap+header->pixelOffset, data, header->fullFileSize);
char fileName[] = "sampleBitmap.bmp";
// write
writeToFile((unsigned char *)bitmap , header->fullFileSize, fileName);
free(header);
free(data);
free(bitmap);
return 0;
}
void handleBitmapHeader(BitmapHeader *header, int width, int height)
{
// Calculate row with padding
int rowWithPadding = closestMultipleOfFour(width*3);
int rawSize = height * rowWithPadding;
printf("Row With Padding : %4d\n", rowWithPadding);
printf("Raw Size : Decimal[%4d], Hex[%4x]\n", rawSize, rawSize);
header->b0 = 'B';
header->b1 = 'M';
header->fullFileSize = rawSize + 54;
header->reserved = 0x00000000;
header->pixelOffset = 54;
header->bitmapInfoHeaderSize = 40;
header->pixelWidth = (unsigned int)width;
header->pixelHeight = (unsigned int)height;
header->numberOfColorPlanes = 1;
header->bitPerPixel = 24;
header->compessionState = 0;
header->sizeOfRawPixel = (unsigned int) rawSize;
header->horizontalResolution = 0x2835;
header->verticalResolution = 0x2835;
header->numberOfColors = 0;
header->numberOfImportantColors = 0;
}
int closestMultipleOfFour(int num)
{
return (num + 3) & ~0x03;
}
void writeToFile(unsigned char* bytes, int len, char fileName[])
{
FILE *filePtr;
printf("HIT A : len = %d\n", len);
filePtr = fopen(fileName, "wb"); // wb for write binary
printf("HIT B\n");
if(!filePtr)
{
printf("ERROR::writeToFile()::Unable to open file : \"%s\"\n", fileName);
return;
}
fwrite(bytes, len, 1, filePtr);
fclose(filePtr);
}
void setPixel(unsigned char *data, int x, int y, unsigned char red, unsigned char green, unsigned char blue, int width, int height)
{
y = height-1-y;
int index = 0;
int padSize = 0;
if(x < width && y == 0) // no need to calculate padding
index = x * 3;
else
{
boolean isPadding = ( (width*3) % 4 == 0) ? FALSE : TRUE;
if(isPadding == TRUE) {
padSize = closestMultipleOfFour(width * 3);
} else {
index = (y*width + x)*3;
}
}
int bytes = 0;
*(data + index+padSize + bytes++) = blue;
*(data + index+padSize + bytes++) = green;
*(data + index+padSize + bytes) = red;
}
Please notify the lines below;
//int width = 800; // compiling OK, runtime OK
int width = 100; // compiling OK, runtime FAILED
int height = 600;
If you just run for 800x600 resolution like;
int width = 800; // compiling OK, runtime OK
/int width = 100; // compiling OK, runtime FAILED
int height = 600;
The program runs sucessfully. And also notify that for both conditions, the program is compiling successfully.
I try with gcc and MS VS, the behaviour is the same for both resolutions.
I've also tried to debug the program with MS VS but while debugging, the program triggered a breakpoint then directly ask me for a dll (I'm not expert on MS VS Debugging with C)
It seems the problematic part is with this line where exist in writeToFile() function;
filePtr = fopen(fileName, "wb"); // wb for write binary
Any ideas on where do I've gone wrong ?
The program crashed because this line is accessing the memory out of bound:
memcpy(bitmap + header->pixelOffset, data, header->fullFileSize);
I think what you need is
memcpy(bitmap + header->pixelOffset, data, header->sizeOfRawPixel);
because bitmap is a buffer contains the header and the raw data.
Related
i decided to learn C, and i try to follow this tutorial http://ricardolovelace.com/creating-bitmap-images-with-c-on-windows.html
but when i try to compile my code with gcc as this >gcc -Wall testc o app
he doesn't know type_rgb, can i define this type and how? and where in my code ?
#include <stdio.h>
struct rgb_data {
float r, g, b;
};
void save_bitmap( const char *file_name, int width, int height, int dpi, type_rgb *pixel_data);
/*
next steps of the tutorial
*/
rgb_data *pixels = new rgb_data[width * height];
for( int x = 0; x < width; x++)
{
for(int y = 0; y < height; y++)
int a = y * width +x;
{
if ((x > 50 && x < 350) && (y > y && y < 350))
{
pixels[a].r = 255;
pixels[a].g = 255;
pixels[a].b = 0;
}else{
pixels[a].r = 55;
pixels[a].g = 55;
pixels[a].b = 55;
}
}
}
save_bitmap("black_border.bmp", width, height, dpi, pixels);
Bitmap file format is rather complicated. This is not the best way to learn C. It's better to start with something much simpler.
Having said that, the bitmap format starts with a bitmap header BITMAPFILEHEADER structure which is 14 bytes long, followed by BITMAPINFOHEADER structure 40 bytes long. These structures are defined in "Windows.h"
You have to write in various information in these structures and write them to file before writing the actual pixels.
You can have 1, 4, 8, 16, 24, and 32-bit bitmap. This is an example to read a 32-bit bitmap. This code assumes sizeof(short) is 2, sizeof(int) is 4.
int main()
{
int row, column;
int width = 100;
int height = 100;
int size = width * height * 4; //for 32-bit bitmap only
char header[54] = { 0 };
strcpy(header, "BM");
memset(&header[2], (int)(54 + size), 1);
memset(&header[10], (int)54, 1);//always 54
memset(&header[14], (int)40, 1);//always 40
memset(&header[18], (int)width, 1);
memset(&header[22], (int)height, 1);
memset(&header[26], (short)1, 1);
memset(&header[28], (short)32, 1);//32bit
memset(&header[34], (int)size, 1);//pixel size
unsigned char *pixels = malloc(size);
for(row = height - 1; row >= 0; row--) {
for(column = 0; column < width; column++) {
int p = (row * width + column) * 4;
pixels[p + 0] = 64; //blue
pixels[p + 1] = 128;//green
pixels[p + 2] = 192;//red
}
}
FILE *fout = fopen("32bit.bmp", "wb");
fwrite(header, 1, 54, fout);
fwrite(pixels, 1, size, fout);
free(pixels);
fclose(fout);
return 0;
}
Note the first pixel is blue, followed by green and read. The last pixel is not used in 32-bit bitmap. Also the height goes from bottom to top. This is another odd feature of bitmap. 24-bit bitmaps are more complicated because they need padding. 8-bit and lower will need an additional palette.
struct rgb_data {
float r, g, b;
};
float is not the right type for pixels. Each color goes from 0 to 255. This fits in unsigned char. You need instead
struct rgb_data {
unsigned r, g, b, alpha;
};
The alpha is the extra byte for 32-bit bitmap (which we won't use). Notice the size of this structure is 4. You can allocate this as
struct rgb_data *rgb = malloc(size);
Now you can access the pixels as follows:
int p = (row * width + column);
rgb[p].r = 255;
rgb[p].g = 0;
rgb[p].b = 0;
...
fwrite(rgb, 4, width * height, fout);
I am building a function that replaces a color in a BMP image with another target color.
It works as long as I don't attempt to replace a color in image that requires padding.
However, I am almost certain that the way I account padding is correct.. So for me this is a complete mystery.
330 x 250 (248 054 bytes) Hi-color 24bit bitmap
This is the function:
union
{
unsigned long ulColor;
unsigned char byteColor[4];
} oldColor;
union
{
unsigned long ulColor;
unsigned char byteColor[4];
} newColor;
typedef unsigned char BYTE;
typedef unsigned short int WORD;
typedef unsigned long int DWORD;
typedef unsigned long long int DDWORD;
DDWORD
bitfox_color_replace_data
(BYTE *buff, BYTE old_r, BYTE old_g, BYTE old_b, BYTE new_r, BYTE new_g, BYTE new_b)
{
#define OFFSET_OF_SIZE 0x2
#define OFFSET_OF_PIXELS 0xA
#define OFFSET_OF_WIDTH 0x12
#define OFFSET_OF_HEIGHT 0x16
DWORD* buffSize = (DWORD*)&buff[OFFSET_OF_SIZE];
DWORD* buffPixels = (DWORD*)&buff[OFFSET_OF_PIXELS];
DWORD* buffWidth = (DWORD*)&buff[OFFSET_OF_WIDTH];
DWORD buffHeight = 0;
BYTE pad = 0;
DDWORD pixels_replaced = 0;
DDWORD i;
oldColor.byteColor[0] = old_b; newColor.byteColor[0] = new_b;
oldColor.byteColor[1] = old_g; newColor.byteColor[1] = new_g;
oldColor.byteColor[2] = old_r; newColor.byteColor[2] = new_r;
for(i = (*buffPixels); i < (*buffSize); i += 3)
{
if( i == ((*buffPixels) + (((*buffWidth) * 3) + pad) * (buffHeight + 1)) )
{
pad = ((*buffWidth) % 4);
buffHeight++;
i += pad;
}
if(!memcmp(buff + i, oldColor.byteColor, 3))
{
memcpy(buff + i, newColor.byteColor, 3);
pixels_replaced++;
}
}
return pixels_replaced;
}
What am I possibly doing wrong ?
When dealing with bitmaps, there are three parameters that you need to know: height, width, and stride. Width and height are obvious. The stride is the number of bytes per line including padding.
Here's one way to compute the stride. Note that the stride must be a multiple of 4.
int stride = ((width * 3) + 3) >> 2;
stride *= 4;
The first line computes the minimum number of 4-byte values that can hold a line. The second line converts the stride to a byte count.
The code at the end of this post demonstrates how to use the stride. The code assumes that the input file contains a 24bpp RGB image. The file headers have already been read, leaving just the pixel data, which is read into a buffer. The code writes to an output file, assuming the output image is the same size as the input image and any headers have already been written.
The important lines are
size = height * stride; // total number of bytes in the image, including padding
offset = (y * stride) + (x * 3); // 'y * stride' is the offset to the beginning of a line
// 'x * 3' computes the byte offset of a particular pixel
for ( x=width*3; x<stride; x++ ) // outputs the padding bytes, if needed,
fputc( 0, fpout );
unsigned char *buffer = NULL;
int height = bmpinfo.biHeight;
int width = bmpinfo.biWidth;
// stride = (width * 3), rounded up to a multiple of 4
int stride = ((width * 3) + 3) >> 2;
stride *= 4;
// size of the pixel data, including padding
size_t size = height * stride;
// allocate memory for the pixel data
if ( (buffer = malloc( size )) == NULL )
error( "Insufficient memory" );
// read the pixel data from the file
if ( fread( buffer, 1, size, fpin ) != size )
error( "Unable to read image data" );
// process pixels by row and column
for ( y = 0; y < height; y++ )
{
for ( x = 0; x < width; x++ )
{
// get the RGB values from the buffer
offset = (y * stride) + (x * 3);
blue = buffer[offset];
green = buffer[offset+1];
red = buffer[offset+2];
// mess around with the RGB value here
// write the new RGB values to the file
fputc( (int)blue , fpout );
fputc( (int)green, fpout );
fputc( (int)red , fpout );
}
// write the padding bytes to the file
for ( x = width*3; x < stride; x++ )
fputc( 0, fpout );
}
Below is a small test program (works on little endian machines).
As is, the result is already strange to me :
in: r=20 g=20 b=80 a=FF (#202080FF, ok!)
out: r=90 g=90 b=C0 a=FF (#9090C0FF, strange...)
Where as I expected the fill color #FFFFFFFF x the mask 0x80 = #FFFFFF80 and so an output of #9090FFFF...
Now, if I set the fill color to #FFFFFF80 by changing "cfill.alpha = uint16_t(0x80) << 8;" , the result seems really wrong :
in: r=20 g=20 b=80 a=FF
out: r=98 g=98 b=E0 a=FF
I would expect fill x mask => #FFFFFF40 and thus an output of: #606060C0FF.
I especially do not understand how a lower alpha input color can end up in a lighter output on the target image.
What I am doing wrong here ?
Is there another PIXMAP_OP_xxx that would work as I expect ?
Thanks.
#include <stdlib.h>
#include <stdio.h>
#include "pixman.h"
union C {
uint32_t value;
struct RGBA8888 {
uint8_t a;
uint8_t b;
uint8_t g;
uint8_t r;
} rgba;
};
int main()
{
// create target image full with r=0x20 g=0x20 b=0x80 a=0xFF
size_t w = 100; // multiple of 4 for alignment
size_t h = 100;
C *target = (C*)malloc(w * h * sizeof(C));
for(size_t i = 0; i < w * h; ++i)
target[i].value = 0x202080FF;
printf("in: r=%02X g=%02X b=%02X a=%02X\n",
target[0].rgba.r, target[0].rgba.g, target[0].rgba.b, target[0].rgba.a);
// connect target to pixman image
pixman_image_t *ptarget = pixman_image_create_bits(PIXMAN_r8g8b8a8, w, h, (uint32_t*)target, w * sizeof(uint32_t));
// create fill
pixman_color_t cfill;
cfill.red = uint16_t(0xFF) << 8;
cfill.green = uint16_t(0xFF) << 8;
cfill.blue = uint16_t(0xFF) << 8;
cfill.alpha = uint16_t(0xFF) << 8;
pixman_image_t *pfill = pixman_image_create_solid_fill(&cfill);
// create mask with a=0x80
uint8_t *mask = (uint8_t*)malloc(w * h);
for(size_t i = 0; i < w * h; ++i)
mask[i] = 0x80;
pixman_image_t *pmask = pixman_image_create_bits(PIXMAN_a8, w, h, (uint32_t*)mask, w);
// do compositing
pixman_image_composite(
PIXMAN_OP_OVER,
pfill, pmask, ptarget,
// src_x, src_y
0, 0,
// mask_x, mask_y
0, 0,
// dest_x, dest_y, width, height
0, 0, w, h);
// display one pixel of target
printf("out: r=%02X g=%02X b=%02X a=%02X\n",
target[0].rgba.r, target[0].rgba.g, target[0].rgba.b, target[0].rgba.a);
}
I turns out that Pixman works with premultiplied alpha !
So the white with alpha should be #80808080 and subsequently #40404040 and not #FFFFFF80 and #FFFFFF40.
Hope it helps somebody else ;)
As we all know each byte is unsigned char which means with a range of 0 - 255.
I wrote a code that will write BMPs, the code works.. the problem is when i try to write a bmp with width/height more then 255 it appears as a value less then 255.
On the next bit of the width i just putted 1 and it works. It outputs the real with (500) but i wish that could work for the height too.. but it doesn't. The image goes broken.
Update: Messing with height more then 214 leeds to a damaged bmp. What do i miss?
Here is my code tho:
typedef unsigned char BYTE; //1
typedef unsigned short WORD; //2
typedef unsigned long DWORD; //4
typedef struct BMP_HEADER
{
BYTE bmp_type[2]; //2
BYTE bmp_size[4]; //4
BYTE bmp_as[4]; //4
BYTE bmp_offset[4]; //4
} BMP;
typedef struct DIB_HEADER
{
BYTE dib_size[4]; //4
DWORD dib_BMPwidth; //4
DWORD dib_BMPheight; //4
BYTE dib_BMPcplanes[2]; //2
BYTE dib_BMPBPX[2]; //2
BYTE dib_BIRGB[4]; //4
BYTE dib_rawsize[4]; //4
BYTE dib_Xresolution[4]; //4
BYTE dib_Yresolution[4]; //4
BYTE dib_cpalette[4]; //4
BYTE dib_cimportant[4]; //4
} DIB;
typedef struct PIXEL_ARRAY
{
BYTE BGR[3]; //3
} PIX;
BYTE* PAD;
DWORD padding(DWORD BMPwidth)
{
int pitch = BMPwidth * 3;
if (pitch % 4 != 0)
{
pitch += 4 - (pitch % 4);
}
return pitch - (BMPwidth * 3);
}
void create_bmp(char BMPname[], DWORD BMPwidth, DWORD BMPheight, WORD BMPR, WORD BMPG, WORD BMPB)
{
// variables
char build_name[256];
FILE* fp;
BMP newBMP;
DIB newDIB;
PIX* template;
gedMaxGameMem = 2827465479;
if(BMPwidth > 1 && BMPheight > 1) template = (PIX*)malloc(BMPwidth*BMPheight);
else template = (PIX*)malloc(sizeof(PIX*)*max(BMPwidth, BMPheight));
// make file
sprintf(build_name, "%s.bmp", BMPname);
fp = fopen(build_name, "wb");
// BM
newBMP.bmp_type[0] = 'B';
newBMP.bmp_type[1] = 'M';
fwrite(&newBMP.bmp_type, 1, 2, fp);
{
// SIZE
DWORD sz = (sizeof(newBMP) + sizeof(newDIB) + sizeof(template));
fwrite(&sz, 1, 1, fp);
}
// Application specifics
fwrite(&newBMP.bmp_as, 1, 4, fp);
// offset
newBMP.bmp_offset[0] = (sizeof(newBMP) + sizeof(newDIB));
fwrite(&newBMP.bmp_offset, 1, 4, fp);
//
// DIB SIZE
newDIB.dib_size[0] = sizeof(newDIB);
fwrite(&newDIB.dib_size, 1, 4, fp);
// DIB Image width
newDIB.dib_BMPwidth = BMPwidth;
fwrite(&newDIB.dib_BMPwidth, 1, 4, fp);
// DIB Image height
newDIB.dib_BMPheight = BMPheight;
fwrite(&newDIB.dib_BMPheight, 1, 4, fp);
// DIB Color planes
newDIB.dib_BMPcplanes[0] = 1;
fwrite(&newDIB.dib_BMPcplanes, 1, 2, fp);
// DIB BMP Bits per pixel
newDIB.dib_BMPBPX[0] = 24;
fwrite(&newDIB.dib_BMPBPX, 1, 2, fp);
// DIB Compression method
fwrite(&newDIB.dib_BIRGB, 1, 4, fp);
// DIB Raw size
newDIB.dib_rawsize[0] = sizeof(template);
fwrite(&newDIB.dib_rawsize, 1, 4, fp);
// DIB Xresolution
newDIB.dib_Xresolution[0] = 19;
newDIB.dib_Xresolution[1] = 11;
fwrite(&newDIB.dib_Xresolution, 1, 4, fp);
// DIB Yresolution
newDIB.dib_Yresolution[0] = 19;
newDIB.dib_Yresolution[1] = 11;
fwrite(&newDIB.dib_Yresolution, 1, 4, fp);
// DIB Colors palette
fwrite(&newDIB.dib_cpalette, 1, 4, fp);
// DIB Important colors
fwrite(&newDIB.dib_cimportant, 1, 4, fp);
//
{
int i;
for(i=0; i<BMPwidth+1; i++)
{
template[i].BGR[0] = BMPB;
template[i].BGR[1] = BMPG;
template[i].BGR[2] = BMPR;
fwrite(template->BGR, 3, i, fp);
}
}
free(template);
fclose(fp);
}
If you want to fiddle with the width and height as bytes, you can't just set the second byte to one, you have to calculate the exact value, e.g.:
uint32_t h = 1024;
height[0] = h & 0xff;
height[1] = (h >> 8) & 0xff;
height[2] = (h >> 16) & 0xff;
height[3] = (h >> 24) & 0xff;
(The masking with & 0xff is really unneccessary here, that's done anyway when fitting the number into a byte.)
But maybe you should just use larger integer types in your structure in the first place:
typedef struct DIB_HEADER
{
DWORD dib_size;
DWORD dib_BMPwidth;
DWORD dib_BMPheight;
WORD dib_BMPcplanes];
...
};
(This might lead to problems with endianness, but I think Windows bitmaps are stored in Little-Endian format, so it shouldn't be an issuehere.)
Edit: Looking at your code a bit further, I see some errors:
If you want to allocate temporary memory for the pixels, you must allocate 3 bytes for each pixel, i.e. sizeof(PIX) * BMPwidth + BMWheight. Your distinction of single-row, single columns and other bitmaps ist pointless and also introduces an error (namely not allocating the size of each pixel for each pixel.)
You are writing a monochrome bitmap of one colour only, so you don't really need to create a huge temporary bitmap; you can write the same pixel over and over again.
You create the BMP and DIB structures on the stack; they might therefore be uninitialised and have garbage in the fields that you don't initialise explicitly.
You don't have to write every field on its own, you can write whole structures instead. (But you'll have to take out the "BM" mark, because the structure will be padded there to make the next field begin at a four-byte-border. You could also play with compiler options to always pack your structs tightly.)
When you write out the data, you only loop over the width, but you must loop over both width and height in a nested loop.
When you write out data with fwrite, your arguments are wrong: You probably want to write fwrite(&template[i]->RGB, 3, 1, fp). But your item count is i which means that you are writing everything from the beginning over and over again, but always attaching the last byte, like ´AABABCABCDABCDE`. (This doesn't matter when all pixels have the same colour, but your file size will be off.)
BMP line data is padded to 4 bytes. You have written a routine for this, but don't call it.
In other words. You code isn't prepared for data that has more than one line. :-)
Here's a simpler version of your code that write a single-colour bitmap:
void create_bmp_uni(const char *fn, int w, int h, int r, int g, int b)
{
FILE* fp;
DWORD rsize = (w * sizeof(PIX) + 3) / 4 * 4;
DWORD pad = rsize - w * sizeof(PIX);
DWORD rawsize = rsize * h * sizeof(PIX);
BYTE zero[3] = {0};
const char *id = "BM";
BMP bmp = {
2 + sizeof(BMP) + sizeof(DIB) + rawsize,
0,
2 + sizeof(BMP) + sizeof(DIB)
};
DIB dib = {
sizeof(DIB),
w,
h,
1,
24,
0,
rawsize,
2835,
2835,
0,
0
};
PIX pix = {b, g, r};
int i, j;
fp = fopen(fn, "wb");
fwrite(id, 1, 2, fp);
fwrite(&bmp, 1, sizeof(bmp), fp);
fwrite(&dib, 1, sizeof(dib), fp);
for(i = 0; i < h; i++) {
for(j = 0; j < w; j++) {
fwrite(&pix, sizeof(pix), 1, fp);
}
if (pad) fwrite(zero, 1, pad, fp);
}
fclose(fp);
}
(The .bmp suffix has to be added by the caller. Also note how the BM marker is written separately from the rest of the BMP struct.)
Output:
http://i.stack.imgur.com/fqxO9.png (happans each time with different colors)
Current code:
typedef unsigned char BYTE; //1
typedef unsigned short WORD; //2
typedef unsigned long DWORD; //4
typedef struct BMP_HEADER
{
BYTE bmp_size[4]; //4
BYTE bmp_as[4]; //4
BYTE bmp_offset[4]; //4
} BMP;
typedef struct DIB_HEADER
{
BYTE dib_size[4]; //4
DWORD dib_w; //4
BYTE dib_h[4]; //4
BYTE dib_BMPcplanes[2]; //2
BYTE dib_BMPBpix[2]; //2
BYTE dib_BIRGB[4]; //4
BYTE dib_rawsize[4]; //4
BYTE dib_Xresolution[4]; //4
BYTE dib_Yresolution[4]; //4
BYTE dib_cpalette[4]; //4
BYTE dib_cimportant[4]; //4
} DIB;
//_______________________________________________________________________________________________//
typedef struct PIXEL_ARRAY
{
BYTE BGR[3]; //3
} PIX;
BYTE* PAD;
DWORD padding(DWORD w)
{
int pitch = w * 3;
if (pitch % 4 != 0)
{
pitch += 4 - (pitch % 4);
}
return pitch - (w * 3);
}
DWORD row_sz(WORD BMPBpix, DWORD w)
{
return ((BMPBpix*w+31)/32)*4;
}
void create_bmp(char BMPname[], DWORD w, DWORD h, BYTE R, BYTE G, BYTE B)
{
// variables
char build_name[256];
FILE* fp;
BMP newBMP;
DIB newDIB;
PIX* pix;
DWORD rsize = (w * sizeof(PIX) + 3) / 4 * 4;
DWORD pad = rsize - w * sizeof(PIX);
DWORD rawsize = rsize * h * sizeof(PIX);
BYTE zero[3] = {0};
const char *id = "BM";
int i, j;
gedMaxGameMem = 2827465479;
if(w > 1 && h > 1) pix = (PIX*)malloc(w*h);
else pix = (PIX*)malloc(sizeof(PIX*)*max(w, h));
// make file
sprintf(build_name, "%s.bmp", BMPname);
fp = fopen(build_name, "wb");
// BM
fwrite(id, 1, 2, fp);
// SIZE
newBMP.bmp_size[0] = (sizeof(newBMP) + sizeof(newDIB) + sizeof(pix));
fwrite(&newBMP.bmp_size, 1, 4, fp);
// Application specifics
fwrite(&newBMP.bmp_as, 1, 4, fp);
// offset
newBMP.bmp_offset[0] = (sizeof(newBMP) + sizeof(newDIB));
fwrite(&newBMP.bmp_offset, 1, 4, fp);
//
// DIB SIZE
newDIB.dib_size[0] = sizeof(newDIB);
fwrite(&newDIB.dib_size, 1, 4, fp);
// DIB Image width
newDIB.dib_w = w;
fwrite(&newDIB.dib_w, 1, 4, fp);
// DIB Image height
newDIB.dib_h[0] = h;
fwrite(&newDIB.dib_h, 1, 4, fp);
// DIB Color planes
newDIB.dib_BMPcplanes[0] = 1;
fwrite(&newDIB.dib_BMPcplanes, 1, 2, fp);
// DIB BMP Bits per pixel
newDIB.dib_BMPBpix[0] = 24;
fwrite(&newDIB.dib_BMPBpix, 1, 2, fp);
// DIB Compression method
fwrite(&newDIB.dib_BIRGB, 1, 4, fp);
// DIB Raw size
newDIB.dib_rawsize[0] = sizeof(pix);
fwrite(&newDIB.dib_rawsize, 1, 4, fp);
// DIB Xresolution
newDIB.dib_Xresolution[0] = 19;
newDIB.dib_Xresolution[1] = 11;
fwrite(&newDIB.dib_Xresolution, 1, 4, fp);
// DIB Yresolution
newDIB.dib_Yresolution[0] = 19;
newDIB.dib_Yresolution[1] = 11;
fwrite(&newDIB.dib_Yresolution, 1, 4, fp);
// DIB Colors palette
fwrite(&newDIB.dib_cpalette, 1, 4, fp);
// DIB Important colors
fwrite(&newDIB.dib_cimportant, 1, 4, fp);
//
for(i = 0; i < h; i++)
{
for(j = 0; j < w; j++)
{
fwrite(&pix, sizeof(pix), 1, fp);
}
if (pad) fwrite(zero, 1, pad, fp);
}
fclose(fp);
}
I am trying to track objects inside an image using histogram data from the object. I pass in a reference image to get the histogram data and store it in a Mat. From there I load in an image and try and use the histogram data to detect the object. The problem I am coming with is not only is it not tracking the object, but it's not updating the detection. If I load image "1.jpg" the detection will claim that the object is in the top right corner when it's in the bottom left. When I pass in the second image the detection field does not move at all. This continues for the next batch of images as well. Below is a code snippet of my application.
This is being done in a Windows 7 32-bit environment using OpenCV2.3 in VS2010. Thanks in advance for any help
int main( int argc, char** argv )
{
vector<string> szFileNames;
IplImage* Image;
Mat img, hist, backproj;
Rect trackWindow;
// Load histogram data
hist = ImageHistogram("C:/Users/seb/Documents/redbox1.jpg", backproj);
Image = cvLoadImage("C:/Users/seb/Documents/1.jpg");
img = Mat(Image);
trackWindow = Rect(0, 0, Image->width, Image->height);
imshow("Histogram", hist);
while(true)
{
Detection(img, backproj, trackWindow);
imshow("Image", img);
char c = cvWaitKey(1);
switch(c)
{
case 32:
{
cvReleaseImage(&Image);
Image = cvLoadImage("C:/Users/seb/Documents/redbox2.jpg");
img = Mat(Image);
break;
}
}
}
cvReleaseImage(&Image);
// Destroy all windows
cvDestroyWindow("Histogram");
cvDestroyWindow("Image");
return 0;
}
Mat ImageHistogram(string szFilename, Mat& backproj)
{
// Create histogram values
int vmin = 10;
int vmax = 256;
int smin = 30;
int hsize = 16;
float hranges[] = {0,180};
const float* phranges = hranges;
// Load the image
IplImage* Image = cvLoadImage(szFilename.c_str());
Rect rect = Rect(0, 0, Image->width, Image->height);
// Convert Image to a matrix
Mat ImageMat = Mat(Image);
// Create and initialize the Histogram
Mat hsv, mask, hue, hist, histimg = Mat::zeros(200, 320, CV_8UC3);
cvtColor(ImageMat, hsv, CV_BGR2HSV);
// Create and adjust the histogram values
inRange(hsv, Scalar(0, smin, vmin), Scalar(180, 256, vmax), mask);
int ch[] = {0, 0};
hue.create(hsv.size(), hsv.depth());
mixChannels(&hsv, 1, &hue, 1, ch, 1);
Mat roi(hue, rect), maskroi(mask, rect);
calcHist(&roi, 1, 0, maskroi, hist, 1, &hsize, &phranges);
normalize(hist, hist, 0, 255, CV_MINMAX);
histimg = Scalar::all(0);
int binW = histimg.cols / hsize;
Mat buf(1, hsize, CV_8UC3);
for( int i = 0; i < hsize; i++ )
buf.at<Vec3b>(i) = Vec3b(saturate_cast<uchar>(i*180./hsize), 255, 255);
cvtColor(buf, buf, CV_HSV2BGR);
for( int i = 0; i < hsize; i++ )
{
int val = saturate_cast<int>(hist.at<float>(i)*histimg.rows/255);
rectangle( histimg, Point(i*binW,histimg.rows),
Point((i+1)*binW,histimg.rows - val),
Scalar(buf.at<Vec3b>(i)), -1, 8 );
}
calcBackProject(&hue, 1, 0, hist, backproj, &phranges);
backproj &= mask;
cvReleaseImage(&Image);
return histimg;
}
void Detection(Mat& image, Mat& backproj, Rect& trackWindow)
{
RotatedRect trackBox = CamShift(backproj, trackWindow, TermCriteria( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1 ));
int test2 = trackWindow.area();
if(trackBox.size.height > 0 && trackBox.size.width > 0)
{
if( trackWindow.area() <= 1 )
{
int cols = backproj.cols, rows = backproj.rows, r = (MIN(cols, rows) + 5)/6;
trackWindow = Rect(trackWindow.x - r, trackWindow.y - r,
trackWindow.x + r, trackWindow.y + r) &
Rect(0, 0, cols, rows);
}
int test = trackBox.size.area();
if(test >= 1)
{
rectangle(image, trackBox.boundingRect(), Scalar(255,0,0), 3, CV_AA);
ellipse( image, trackBox, Scalar(0,0,255), 3, CV_AA );
}
}
}
I've figured out the issue. It had to deal with me not converting the image that I'm checking upon. I had to get histogram data from my colored box and then I had to get the histogram from the image I was using to search.