I'm making a program that creates a bitmap file in C. it's using 24-bit colour.
I'm writing the file in 3 stages, i first write the FileHeader, then the InfoHeader, and then the Pixel Data. I'm having trouble padding the pixel data so each row finishes on a word boundary.
The code below works sometimes, but only without the while loop (which adds the padding to the end of the line). For example, with a 12x12px image, I can scale it to 24x24, but not to 10x10 (the file is corrupt). When I put in the padding code below, the image becomes distorted, and sometimes gets corrupted too.
I can't seem to figure out what's going wrong, the code below should add padding to the end of each line until i hits a word boundary, and then starts the next line.
fwrite(&fh, 1, sizeof(FILEHEADER), n_img);
fwrite(&ih, 1, sizeof(INFOHEADER), n_img);
int i, j;
uint8_t pad = 0;
for (i = height-1; i >= 0; i--) {
for (j = 0; j < width; j++)
fwrite(n_pix+(i*width)+j, 1, sizeof(IMAGE), n_img);
while(ftell(n_img)%4 != 0)
fwrite(&pad, 1, 1, n_img);
}
You are not padding rows to word size, you are padding the current file position. And it doesn't work because the size of your headers add up to 54 -- not a multiple of 4.
Instead of using ftell to retrieve the 'current position', use maths. Make your pad an unsigned long, and insert before your loops:
int npad = (sizeof(IMAGE)*width) & 3;
if (npad)
npad = 4-npad;
Then, instead of the while(ftell .. loop, write out the number of required bytes immediately:
fwrite (&pad, 1,npad, n_img);
npad will range from 0..3, that's why you have to make pad a 4-byte integer.
Related
I'm trying to create a 2d array that contains RGB value for each pixel in the image,
I created a copy of the original image to check out if the images are similar and I got that the output is mostly gray pixels.
(I try to use only standard libraries).
that's the structs I use
typedef struct { //bmp file values struct
int width;
int height;
} image_t;
typedef struct { //pixel color
unsigned char r;
unsigned char g;
unsigned char b;
} color_t;
the main
int main() {
int i, j;
color_t** matrix;
static image_t image;
if ((LoadSprite(&image, BMP)) != 0) {
printf_s("Failed to load file: \" %s\"", BMP);
return -1;
}
matrix = malloc(sizeof(color_t*) * image.height); // allocate memory to image pixel matrix
for (i = 0;i < image.height;i++) {
matrix[i] = malloc(sizeof(color_t) * image.width);
}
imgtrx(matrix, image, BMP);
CreateBMP(BMPCPY, matrix, image.height, image.width);
return 0;
}
the function imgtrx essentially assigns RGB pixel value to the correct location in the matrix according to the image height & width
void imgtrx(color_t** mtrx, image_t image, char* filename) {
int val, t, i = 0, j, k = 0;
FILE* file;
val = fopen_s(&file, filename, "rt");
//fseek(file, 54, SEEK_SET);
fseek(file, 10, SEEK_SET);
fread(&t, 1, 4, file); //reads the offset and puts it in t
fseek(file, t, SEEK_SET);
int p, e;
for ( i = 0; i < image.width; i++)
{
for (j = 0;j < image.height;j++) {
fread(&mtrx[i][j].r, 8, 1, file);
fread(&mtrx[i][j].g, 8, 1, file);
fread(&mtrx[i][j].b, 8, 1, file);
}
}
fclose(file);
return 0;
}
the following function converts the 2d array to single dimensions and writes a BMP copy
void CreateBMP(char* filename, color_t** matrix, int height, int width)
{
int i, j;
int padding, bitmap_size;
color_t* wrmat;
wrmat = malloc(sizeof(color_t) * height * width);
for (i = 0;i < height;i++) {
for (j = 0;j < width;j++) {
wrmat[i + j] = matrix[i][j];
}
}
if (((width * 3) % 4) != 0) {
padding = (width * 3) + 1;
}
else
{
padding = width * 3;
}
bitmap_size = height * padding * 3;
char tag[] = { 'B', 'M' };
int header[] = {
0x3a, 0x00, 0x36,
0x28, // Header Size
width, height, // Image dimensions in pixels
0x180001, // 24 bits/pixel, 1 color plane
0, // BI_RGB no compression
0, // Pixel data size in bytes
0x002e23, 0x002e23, // Print resolution
0, 0, // No color palette
};
header[0] = sizeof(tag) + sizeof(header) + bitmap_size;
FILE* fp;
fopen_s(&fp, filename, "w+");
fwrite(&tag, sizeof(tag), 1, fp);
fwrite(&header, sizeof(header), 1, fp); //write header to disk
fwrite(wrmat, bitmap_size * sizeof(char), 1, fp);
fclose(fp);
fclose(fp);
free(wrmat);
}
Note that BMP files can have different pixel formats as detailed in the BITMAPINFOHEADER family of structures documented in the Microsoft manuals for Win32 and OS/2 .
From your "all grey" result, I suspect you are trying to interpret a less than 24bpp format as RGB values, or have run into a technical problem reading the pixel area of the file.
So to do what you are trying to do, your code needs to read the first 4 bytes of the BITMAPINFOHEADER structure, use that to determine the structure version, then read in the rest of the BITMAPINFORHEADER to determine the pixel array format and the size/format of the palette/color information structures that lie between the BITMAPINFOHEADER and the actual pixels.
Also remember to convert any header fields (including the offset field you already parse) from little endian disk format to whatever your runtime CPU uses.
Please note the use of "negative height value" to indicate reversed scanline order is extremely common, as most PC and television hardware stores the top left pixel first, while positive height BMP files store the bottom left pixel first.
The BITMAPINFOHEADER structure versions to worry about are these:
2. BITMAPCOREHEADER (from the 1980s)
3. BITMAPINFOHEADER (Since Windows 3.00)
4. BITMAPV4HEADER
5. BITMAPV5HEADER (Note that the documentation has typos in the linked color profile description).
The pixel formats to worry about are:
1bpp (black and white, with a mandatory palette indicating the RGB equivalent of 0 and 1 bits), as in CGA/EGA/VGA hardware, the most significant bit of each byte is the leftmost (essentially a big endian pixel format).
4bpp (traditionally standard CGA colors, but the palette can specify any other RGB values for the 16 possible pixel codes). The most significant 4 bits of each byte are the leftmost pixel (essentially a big endian pixel format). This format is also used for 3bpp EGA hardware pixels and 2bpp CGA pixels, but using only 8 or 4 of the 16 values.
8bpp (the mandatory palette indicates how each byte value maps to RGB values). VGA hardware included a dedicated chip (the RAMDAC) to do the RGB mapping at the full pixel speed of the monitor.
16bpp (the mandatory palette is an array of 3 unit32_t bitmasks to AND with each pixel to get back only the bits storing the B, G and R values, converting those masks to appropriate shift values is left as an exercise for every graphics programmer). Each pixel is a little endian 2-byte value to be ANDed with the 3 mask values to get the Blue, Green and Red channel values. Most common bitmask values are those for 5:5:5 15bpp hardware and those for 5:6:5 hardware with 1 more green bit.
24bpp (there is no palette or mask data) each pixel is 3 bytes in the order Blue, Green, Red, each giving the color values as a fraction of 255.
32bpp is the same as 16bpp, only with 4 bytes in each pixel value. Common formats described by the mask are Blue,Green,Red,x and Red,Green,Blue,x where x may be 0, random noise or an alpha channel. There are also a few files that use more than 8 bits per color channel to encode HDR images. If you are writing a generic decoder, you need to at least extract the 8 most significant bits of each color channel, though keeping extra pixel bits is rarely necessary.
On top of all these color formats, the HEADER may indicate any of multiple compression formats, most notably the historic BMP RLE4 and BMP RLE8 compressions used by Windows 3.x and the use of common 3rd party compressions such as JPEG, however using BMP format as a wrapper around compressed images is quite rare these days, so you probably don't need to worry, it is however the traditional way to ask the GPU or laser printer to decompress JPEG and PNG files for you, by passing the contents of an in-memory JPEG (from a standard JFIF file) along with an in-memory BITMAPINFOHEADER structure to a GDI or GPI API after checking that other GDI/GPI APIs report that the installed driver supports this.
I am overlaying a custom drawn view on top of a video frame and sending that overlay pixel information to my microcontroller and encoding it into a byte array. Most of the RGB values are 0 because they are apart of the video frame and not the custom view I am drawing.
I am encoding the 32-bit rgb value into 4 different bytes, for each pixel. However, the frame is very slow and lags because I am looping through the entire frame and converting each value to a byte array. I understand what's causing the issue, but wondering if there is a way to speed it up.
I would remove the 0's and only pass values that have a valid RGB value, but I need to keep the position.
The matrix size has 518400 elements.
for (int i = 0; i < A.size(); i++) {
byte *t = (byte *) &A.mem[i];
byte t1 = t[0];
byte t2 = t[1];
byte t3 = t[2];
byte t4 = t[3];
if (t1 != '\0' || t2 != '\0' || t3 != '\0' || t4 != '\0') {
writeSerialData(t, 4);
}
}
You could check the four bytes against zero in one go:
int iMax = A.size();
for (int i = 0; i < iMax; ) {
int32_t *t = (int32_t *) &A.mem[i++];
if (*t) {
writeSerialData(t, 4);
}
}
Do you control the receiving part of that writeSerialData()? Then you should invent some encoding that specifies a new offset of the following data.
Also, are you using 16 millions colors in your overlay? If you are only using a few colors, they invented palette for that. If you know the colors upfront, you can hard-code them on both ends. If not - send a small header with that palette info before the image. That can cut the size of your data by 4 times. If you only have single color, you can further reduce your data to single bits.
UPDATE
By "encoding" I mean some special format of your data, other than simple RGB value. For example, do you use the Alpha channel (I noticed you didn't call your colors RGBA)? If not - you can use that byte for length-encoding, stating that the following color is repeated that many times (up to 255). This will save you some bandwidth on your zeroes (just say how many are there), as well as on a line of the same color (you don't need to send every pixel).
I would like a matrix with each row corresponding to a pixel and each column being an R G B value from a PPM P3 image.
I attempted to create a Matrix with [imageWidth * imageHeight] [3] as the size then populate that but it seems to be taking forever. I think I have missed something, can anyone explain where I went wrong or perhaps suggest a better way to do this?
int pixels [imageSize] [3];
while(fgets(line,70,fd) != NULL){
for ( column = 0; column < (imageSize); column++){
for ( row = 0; row < 1; row++){
sscanf(line, "%d %d %d", &r, &g, &b );
pixels [column] [row] = r;
pixels [column] [row + 1] = g;
pixels [column] [row + 2] = b;
}
}
}
Line refers to the line read in by the fgets function.
The problem is clear in your revised code: you try to read all pixels of the image from each raster line. This won't run forever unless the fgets() blocks indefinitely, but if the raster dimensions are large and the file is organized with many lines of pixels then it may take a very long time. In any case, it's wrong, for on each iteration of the outermost loop -- that is, for each line read -- it overwrites all previously assigned pixel values.
Furthermore, your innermost loop, though not actually wrong, is highly obfuscatory. It always performs exactly one iteration; its iteration variable row in fact indexes pixel components (as opposed to rows of the raster), and row anyway has the value 0 on every iteration. It would be better to just remove it.
Overall, you're making this much harder than it needs to be. The PPM P3 format is designed in a way that makes it amenable to input via much simpler code. Each sample in a valid PPM P3 file is guaranteed to have whitespace before and after. On the other hand, the position and width of the fields and the number of them per line is not fixed. Together, those characteristics makes this one of the comparatively rare cases in which fscanf() is actually a better choice than fgets() + sscanf(). The line length limit makes the latter pretty good too, but you don't need the extra complication that brings.
Given that you want to read imageSize pixels from the FILE designated by fd, with that FILE initially positioned at the first character of the first line of the raster, this will do the trick:
for (pixel = 0; pixel < imageSize; pixel++) {
int numScanned = fscanf(fd, "%d %d %d",
&pixels[pixel][0],
&pixels[pixel][1],
&pixels[pixel][2]);
if (numScanned < 3) {
// ... handle file format or I/O error ...
break;
}
}
I know there is a naive algorithm that is order N and I'm about convinced that's the only one to use. Is there any other that is:
Asymptotically better
Pipelineable i.e. RAW,WAR friendly
Multithreadable.
I'm sure there is one for (1) but I'm not so sure about (2) and (3). If you also want to mention why this is a good interview question. I'd love to know that as well.
The obvious method is easy to do in-place
void remove_every_kth(char *s, size_t len, int k)
{
// UNTESTED CODE, there might be an off-by-one or a wrong loop boundary
if (k < len)
return;
const char *endp = s + len;
size_t offset = 1;
// we skip the s[i] = s[i] memmove at offset=0.
for (s+=k-1 ; s + offset < endp-(k-1) ; s+=k-1) {
// every iteration copies k-1 characters, and advances s by k-1
memmove(s, s+offset, k-1);
offset++;
}
size_t lastchunk = endp - (s+offset); // some number (less than k) of bytes left in the input
memmove(s, s+offset, lastchunk);
s[lastchunk] = '\0';
}
// equivalently, we could have kept a pointer to the read position,
// like const char* read = s+offset;
// and incremented it by k, while incrementing s by k-1
// for (int i=0 ; i < k ; i++) // implicit length string
// if (!s[i]) return; // check for length < k
Since k is constant, you can calculate where to find the input character for any output position. out[i] = in[i + i/k]. There's nothing data-dependent, so this is certainly multithreadable if you don't need to do it in-place, and you have the length of the string in advance. Just farm out the necessary memcpy calls to multiple threads. (I wrote the simple version with memmove instead of a char-pointer loop to make this more obvious, as well as for much better performance with medium to large k. It probably sucks for small k.)
For multithreaded in-place, there's something to gain if k is large, so that even towards the end of a long string, the source and destination of most of the copying is within the same chunk. Each work unit does:
don't touch the first offset = chunk_number * chunk_size / k bytes, the previous work unit needs to read them.
save the second offset bytes to a temp array.
memmove(chunk + offset, chunk + offset*2, chunk_size - offset) (i.e. do the memmove for all the bytes that aren't needed by the previous work unit).
spin-wait for the previous chunk to be marked as done by the thread handling it. (Prob. with a separate data structure, because just watching the data at the last overlapping position won't work. It might be overwritten with the same value.)
copy from the temp buffer to the beginning of the chunk, where the data belongs
mark the work chunk as completed.
For small k, in-place multithread is futile, because most of the bytes in a chunk need to be overwritten with bytes from later chunks. (very large chunks help some.)
would appreciate some brainstorming help for one of my assignments. I am to write a program that does basic point processing of a .bmp image. Program will open a .bmp file for reading and writing and will not change any part of the header, but the pixel values in the file according to command line arguments:
-fromrow x, where x specifies the bottommost row to process
-torowx, where x specifies the topmost row to process
-fromcol x, where x specifies the leftmost column to process
-tocol x, where x specifies the rightmost column to process
-op x, where x is one of the following:
- 1 = threshold the image (any pixel value in the specifies range over 127 is changed to 255, and pixel values 127 or less is changed to 0)
- 2 = negative (any pixel value p in the specified range is changed to 255-p)
To process image data, you will need to make use of the following:
- each pixel value is an unsigned char
- the number of rows in the image is stored as an int at position (byte address) 22 in the file
- the number of columns in the image is stored as an int at position (byte address) 18 in the file
- the position at which the pixel data starts is an int stored at position (byte address) 10 in the file
- pixel information is stored row by row, starting from the bottommost row in the image (row 0) and progressing upwards. within a row; pixel information is stored left to right. padding is added to the end of each row to make row length a multiple of 4 bytes (if the row has 479 columns, there is one extra padding at the end of the row before the next row starts)
I'm a bit lost as to how to begin, but I figure I should make a struct bitmap first like so?
struct bitmap {
unsigned int startrow;
unsigned int endrow;
unsigned int startcol;
unsigned int endcol;
}
Can anyone help walk me through what I would need to do for the byte addresses that the assignment references? Any other brainstorming advice would be greatly appreciated as well. Thanks!
You can read raw bytes by opening a file in binary mode:
FILE *fid = fopen("blah.bmp", "rb");
You can then read some amount of data thus:
int num_actually_read = fread(p, sizeof(*p), num_to_read, fid);
where p is a pointer to some buffer. In this case, you probably want p to be of type uint8_t *, because you're dealing with raw bytes mostly.
Alternatively, you can jump around in a file thus:
fseek(fid, pos, SEEK_SET);
I hope this is enough to get you going.
You will need a pointer to point to the byte addresses 22 and 18 of the file. Once you point to those addresses, you will need to dereference the pointer to get the row and column values. Then you have to point your pointer to address 10 and then traverse the pixels one by one.