Bitmap point processing - c

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.

Related

How do you read pixel data from a PPM (P3) image into a Matrix in C?

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;
}
}

how to extract the number of column and the number of row from size of an image

I have to extract a pgm image. All I have is a size of the whole PGM image which is 505
I tried to extract the number of row and number of column from that image.
int size = 505;
At the beginning, I think Number of column should be
int col = 505/8// size of 1 byte
int row = col *8
I don't know if this is correct? Please advise
PGM images can have arbitrary whitespace at-least 1 byte per pixel, plus a header consisting of 3 ascii mumbers and 6 other bytes with optional padding, so given a size of 505 it'd be at-most 495 pixels in any rectangular arrangement, more than that can't be said from size alone, if you can read the first three lines of he image all will be revealed.
The file size is useful - it's gives a buffer size that will be sufficient to store the pixels, but it does not tell you how they are to be arranged.

Padding a Bitmap pixel array

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.

cvTranspose Gives a Different Image Size?

I am new to OpenCV, and I want to transpose a grayscale image but I am getting the wrong output size.
// img is an unsigned char image
IplImage *img = cvLoadImage("image.jpg"); // where image is of width=668 height=493
int width = img->width;
int height = img->height;
I want to transpose it:
IplImage *imgT = cvCreateImage(cvSize(height,width),img->depth,img->nChannels);
cvTranspose(img,imgT);
When I check the images I see that the original image img has a size of 329324, which is correct: 493*668* 1 byte as it is an unsigned char. However imgT has a size of 331328.
I am not really sure where this happened.
EDIT: 1- I am using Windows XP and OpenCV 2.2.
2- By when i check the image, i meant when i see the values of the variable imgT. Such as the imgT->width, imgT->heigt, imgT->size, etc.
This is due to the fact, that OpenCV aligns the rows of the images at 4-byte boundaries. In the first image a row is 668 bytes wide, which is dividable by 4, so your image elements are contiguous.
The second image has a width of 493 (due to the transposing), which is not dividable by 4. The next higher number dividable by 4 is 496, so your rows are actually 496 bytes wide, with 3 unused bytes at the end of each row, to align the rows at 4-byte boundaries. And in fact 496*668 is indeed 331328. So you should always be aware of the fact, that your image elements need not be contiguous (at least they should be contiguous inside a single row).
You can store your image in cv:Mat() and use Mat.t() to transpose it. Rows and columns will be automatically allocated/deallocated so you don't have to worry about the size

image scaling with C

I'm trying to read an image file and scale it by multiplying each byte by a scale its pixel levels by some absolute factor. I'm not sure I'm doing it right, though -
void scale_file(char *infile, char *outfile, float scale)
{
// open files for reading
FILE *infile_p = fopen(infile, 'r');
FILE *outfile_p = fopen(outfile, 'w');
// init data holders
char *data;
char *scaled_data;
// read each byte, scale and write back
while ( fread(&data, 1, 1, infile_p) != EOF )
{
*scaled_data = (*data) * scale;
fwrite(&scaled_data, 1, 1, outfile);
}
// close files
fclose(infile_p);
fclose(outfile_p);
}
What gets me is how to do each byte multiplication (scale is 0-1.0 float) - I'm pretty sure I'm either reading it wrong or missing something big. Also, data is assumed to be unsigned (0-255). Please don't judge my poor code :)
thanks
char *data;
char *scaled_data;
No memory was allocated for these pointers - why do you need them as pointers? unsigned char variables will be just fine (unsigned because it makes more sense for byte data).
Also, what happens when the scale shoots the value out of the 256-range? Do you want saturation, wrapping, or what?
change char *scaled_data; to char scaled_data;
change *scaled_data = (*data) * scale; to scaled_data = (*data) * scale;
That would get you code that would do what you are trying to do, but ....
This could only possibly work on an image file of your own custom format. There is no standard image format that just dumps pixels in bytes in a file in sequential order. Image files need to know more information, like
The height and width of the image
How pixels are represented (1 byte gray, 3 bytes color, etc)
If pixels are represented as an index into a palette, they have the palette
All kinds of other information (GPS coordinates, the software that created it, the date it was created, etc)
The method of compression used for the pixels
All of this is called Meta-data
In addition (as alluded to by #5), pixel data is usually compressed.
You're code is equivalent to saying "I want to scale down my image by dividing the bits in half"; it doesn't make any sense.
Images files are complex formats with headers and fields and all sorts of fun stuff that needs to be interpreted. Take nobugz's advice and check out ImageMagick. It's a library for doing exactly the kind of thing you want.
why you think you are wrong, i see nothing wrong in your algorithm except for not being efficient and char *data; and char *scaled_data; should be unsigned char data; and unsigned char scaled_data;
My understanding of a bitmap (just the raw data) is that each pixle is represented by three numbers one each for RGB; multiplying each by a number <=1 would just make the image darker. If you're trying to make the image wider, you could maby just output each pixle twice (to double the size), or just output every other pixel (to halve the size), but that depends on how its rasterized.

Resources