I need to read a bmp file and display it as a 2d array of 1 and 0
if the pixel is blue the value in the array is 1 and 0 for white.
unsigned int temp;
int i, j, width, hight;
int** bmp;
FILE* pic;
fopen_s(&pic, "fishpool2.bmp", "rb");
pic_size(pic, &width, &hight);
printf_s("width = %d\thight = %d\n", width, hight);
fseek(pic, 54, SEEK_SET);
for (i = 0; i < hight; i++) {
for (j = 0; j < width; j++) {
temp = fgetc(pic);
fgetc(pic);
fgetc(pic);
if (temp >= 155 && temp <= 245) bmp[i][j] = 1;
}
}
for (i = 0; i < hight; i++) {
for (j = 0; j < width; j++) {
printf_s("%d", bmp[i][j]);
}
puts("");
}
this is what I have so far. I didn't include the code part with i allocate memory and getting the height and width of the pic.
I don't know why but when I run the code the blue spots aren't in the correct position.
(I need to read the picture from the bottom left to top right)
Read this.
BMP file format - Wikipedia
The BMP file format is complex with many variations. Which documents did you look at and code? And you seem to be limiting yourself to specific formats and parameters to read, does that match the actual file you are trying to read?
The stride between two consecutive rows is rounded to 4 bytes. From https://en.wikipedia.org/wiki/BMP_file_format#Pixel_array_(bitmap_data)
For file storage purposes, only the size of each row must be a multiple of 4 bytes while the file offset can be arbitrary.
Therefore in your case (width = 110), each line is 330 bytes long. The stride is rounded to the next multiplicity of 4 which is 332. Therefore the program should fetch 332-330 = 2 extra bytes after processing each row:
for (i = 0; i < hight; i++) {
for (j = 0; j < width; j++) {
temp = fgetc(pic);
fgetc(pic);
fgetc(pic);
if (temp >= 155 && temp <= 245) bmp[i][j] = 1;
}
// fetch 2 extra bytes
fgetc(pic);
fgetc(pic);
}
The more robust solution could be:
size_t row_size = width * 3;
size_t row_size_rounded = (row_size + 3) / 4 * 4;
size_t padding = rows_size_rounded - rows_size;
...
for (i = 0; i < hight; i++) {
for (j = 0; j < width; j++) {
...
}
for (size_t p = 0; p < padding; ++p)
fgetc(pic);
}
When approaching a well-known file format, it is usually a good idea not to "reinvent the wheel". Rather, you look for a library which reads/parses this format into something your program can use more easily. If you search on GitHub, for example, you'll find several BMP reading/writing libraries you could use.
Or you could go for the "swiss army knife" which is ImageMagic; see here.
Related
I got an assigment for a set of programs that manipulate PPM image files. One of these programs consists of adding a colored frame of width w around the original image.
It wasn't very hard finding a solution, but I'm curious if it can be optimized as it doesn't seem the most elegant way to do it.
Im using an array pixel ** (inside a struct ppmfile which also contains header data) to store the image data, it is allocated with this function:
pixel **imgdata(int xsize, int ysize)
{
pixel **imgdata;
imgdata = (pixel**)malloc(ysize*sizeof(pixel*));
for(int i = 0; i < ysize; i++)
(imgdata)[i] = (pixel*)malloc(xsize*sizeof(pixel));
return imgdata;
}
This is the function I came up with:
void frame(ppmfile *ppm, int width, pixel color)
{
pixel **new_image = imgdata(ppm->xsize + (width*2), ppm->ysize + (width*2));
//Copying to center of new pixel matrix
for(int i = 0; i < ppm->ysize; i++)
for(int j = 0; j < ppm->xsize; j++)
new_image[width + i][width + j] = ppm->imgdata[i][j];
free_ppmimgdata(ppm);
ppm->imgdata = new_image;
ppm->xsize += width*2;
ppm->ysize += width*2;
//frame sides
for (int i = 0; i < width; i++)
for(int j = 0; j < ppm->xsize; j++)
{
ppm->imgdata[i][j] = color;
ppm->imgdata[ppm->ysize - i - 1][j] = color;
}
//frame top and bottom
for(int i = width; i < ppm->ysize - width; i++)
for (int j = 0; j < width; j++)
{
ppm->imgdata[i][j] = color;
ppm->imgdata[i][ppm->xsize - j - 1] = color;
}
}
But this allocates an entirely new, bigger, array in memory while also having imo too many loops for something this simple... So my question is if it's possible to realloc() this type of matrix, while moving/copying/preserving its contents at the "center" of the new matrix - as (poorly) illustrated here? Are there any other ways you can see to optimize this?
Thanks in advance!
I want to downsample the bitmap of a BMP file by a factor M. I want to obatain the image without aliasing. So in order to achieve it I compute the mean of the MxM pixels in this way:
The problem apears when I try to resize non-squared images because it only compute the mean proprely in a square. For example, if the final image is 300x150, the mean is right until 150x150 pixel. If I had the previous_mean -> new_mean = (previous_mean+value)/2
This is how I actually compute it:
for (i = 0; i < new_height; i++) {
for (j = 0; j < new_width; j++) {
mean.r = bitmap[i*factor][j*factor].r;
mean.g = bitmap[i*factor][j*factor].g;
mean.b = bitmap[i*factor][j*factor].b;
for(k = i*factor; (k < i*factor+factor)&&(k<old_height); k++){
for(l = j*factor; (l < j*factor+factor)&&(l<old_width); l++){
mean.r = (mean.r + bitmap[k][l].r)/2;
mean.g = (mean.g + bitmap[k][l].g)/2;
mean.b = (mean.b + bitmap[k][l].b)/2;
}
}
new_bitmap[i][j] = mean;
mean.r = 0;
mean.g = 0;
mean.b = 0;
}
}
new_bitmap and bitmap are 2-D array of PIXELS, being PIXELS:
typedef struct __attribute__((__packed__)){
unsigned char b;
unsigned char g;
unsigned char r;
} PIXELS;
This is absolutely correct, I were permutating the old_width with the old_heigth.
I have an image of size MN, now I need to create a new image of size PQ where P=2M-1 and Q=2N-1
Here, f(x,y) is my reference image, and have to place as shown in the figure.
Here's my function of writing to new image
void WritePGM(FILE* fp)
{
int i, j;
int p, q;
p = 2*m-1;
q = 2*n-1;
fprintf(fp, "P5\n%d %d\n%d\n", p, q, 255);
for (j = 0; j<n; j++)
for (i = 0; i<m; i++) {
fputc(image1[m,n], fp);
}
fclose(fp);
}
Can you suggest me the way I should rewrite the loop? Thank you.
There are a few things wrong:
[m,n] isn't valid C code.
[m][n] is but this only writes the bottom right pixel to all output.
Your loops are doing a transpose (I think--most images are matrix[YDIM][XDIM]).
You can do this with one set of nested loops, but it's easier with two. Here is something close to what I think you want [I haven't tested it]. It assumes you want it output in video scan line order (i.e. j is the Y dimension and i is the X dimension):
void
WritePGM(FILE * fp)
{
int i;
int j;
int p;
int q;
p = 2 * m - 1;
q = 2 * n - 1;
fprintf(fp, "P5\n%d %d\n%d\n", p, q, 255);
j = 0;
// write top half of image
for (; j < n; j++) {
i = 0;
// output left half of scan line
for (; i < m; i++)
fputc(image1[j][i], fp);
// output right half of scan line (zeroes)
for (; i < p; i++)
fputc(0, fp);
}
// write bottom half of image (zeroes)
for (; j < q; j++) {
for (i = 0; i < p; i++)
fputc(0, fp);
}
fclose(fp);
}
While writing image1[j][i] it gives error: [Error] subscripted value is neither array nor pointer nor vector. image1 is defined globally as unsigned char *image1;
It's easy to change to a one dimensional array. Also, most video images (i.e. still images like .png, .jpg, etc.) use the y/x nomenclature, so I'm switching to that:
void
WritePGM(FILE * fp)
{
int xcur;
int ycur;
int xmax2;
int ymax2;
unsigned char *scanline;
xmax2 = 2 * xmax - 1;
ymax2 = 2 * ymax - 1;
fprintf(fp, "P5\n%d %d\n%d\n", xmax2, ymax2, 255);
ycur = 0;
// write top half of image
for (; ycur < ymax; ycur++) {
scanline = image1 + (ycur * xmax);
xcur = 0;
// output left half of scan line
for (; xcur < xmax; xcur++)
fputc(scanline[xcur], fp);
// output right half of scan line (zeroes)
for (; xcur < xmax2; xcur++)
fputc(0, fp);
}
// write bottom half of image (zeroes)
for (; ycur < ymax2; ycur++) {
for (i = 0; i < xmax2; xcur++)
fputc(0, fp);
}
fclose(fp);
}
The above should work. Here is the equivalent done a bit faster:
void
WritePGM(FILE * fp)
{
int ycur;
int xmax2;
int ymax2;
unsigned char *scanline;
xmax2 = 2 * xmax - 1;
ymax2 = 2 * ymax - 1;
fprintf(fp, "P5\n%d %d\n%d\n", xmax2, ymax2, 255);
unsigned char zeroline[xmax2];
memset(zeroline,0,sizeof(zeroline));
ycur = 0;
// write top half of image
for (; ycur < ymax; ycur++) {
scanline = image1 + (ycur * xmax);
// output left half of scan line
fwrite(scanline,1,xmax,fp);
// output right half of scan line (zeroes)
fwrite(zeroline,1,xmax2 - xmax,fp);
}
// write bottom half of image (zeroes)
for (; ycur < ymax2; ycur++)
fwrite(zeroline,1,xmax2,fp);
fclose(fp);
}
I am trying to edit a BMP file in C. My code works for BMP files with no padding but I am having trouble dealing with padding.
There are a few other questions on BMP files that I have read but most of them use other languages like C# and Java so I didn't find them very useful.
Here is what the pixel array looks like, but much larger:
char bmpArray[MAX] = {B,G,R,B,G,R,B,G,R,0,0,0,
B,G,R,B,G,R,B,G,R,0,0,0,
B,G,R,B,G,R,B,G,R,0,0,0}
The zeros are for padding bytes to make each row divisible by 4, it depends on the pixel width of the image. What I am trying to do is leave these padding bytes the way they are in the same position and only deal with the B,G,R bytes. If I apply edits to the padding values, the resulting image will be distorted.
I made a function that generates the amount of padding bytes based on the width.
It uses this formula 4 - ((width * 3) % 4) and it works as I tested it with images with different width.
I successfully extracted the B, G, R data of the BMP file and put it into an array so I will only post the part of the code I am having trouble with.
int c = 0;
for (int a = 0; a < height; a++) {
for (int b = 0; b < width*3; b++) {
if (bmpArray[a*(width*3)+b] < 127) {
bmpArray[a*(width*3)+b] = 0;
} else {
bmpArray[a*(width*3)+b] = 255;
}
c++;
}
for (int pad = 0; pad < padding; pad++) {
bmpArray[c++] = 0x00;
}
}
What I am trying to do is "draw" each row of the output BMP file and then stop as soon as I reach the end of the row, that is width*3, then after that draw the padding bytes before going to the next row of pixels.
Alternatively, is there a way I can identify the padding pixels using a single for loop and then use an if statement to not modify the padding pixels? For example:
for (int a = 0; a < bmpArraySize; a++) {
paddingBytes = ??? //for example for the first row
// paddingBytes are i + width*3 + 1
// and i + width*3 + 2 and i + width*3 + 3 if padding = 3
if (a = paddingBytes) {
bmpArray[a] = 0x00;
}
else if (bmpArray[a] < 127) {
bmpArray[a] = 0;
}
else {
bmpArray[a] = 255;
}
}
The problem is in this part:
int c = 0;
for (int a = 0; a < height; a++) {
for (int b = 0; b < width*3; b++) {
if (bmpArray[a*(width*3)+b] < 127) {
bmpArray[a*(width*3)+b] = 0;
} else {
bmpArray[a*(width*3)+b] = 255;
}
}
for (int pad = 0; pad < padding; pad++) {
bmpArray[c++] = 0x00; /* ONLY HERE is 'c' updated! */
}
}
At the end of each line, you fill out the padding starting at c, which starts out at 0 and so overwrites the first few bytes of the first line. Then, each next line gets copied but you continue overwriting from the start (where c initially pointed to).
The padding should be added on each line. In the loops, you adjust a and b but you forget to adjust for the padding.
I suggest the more straightforward code (untested!):
for (int a = 0; a < height; a++) {
for (int b = 0; b < width*3; b++) {
if (bmpArray[a*(width*3 + padding)+b] < 127) {
bmpArray[a*(width*3 + padding)+b] = 0;
} else {
bmpArray[a*(width*3 + padding)+b] = 255;
}
}
for (int pad = 0; pad < padding; pad++) {
bmpArray[a*(width*3 + padding) + 3*width + pad] = 0x00;
}
}
is there a way I can identify the padding pixels ..
Yes – in my loop above with adjustments for padding, it automatically skips the padding itself. You can safely remove the explicit 'set padding to 0' loop at the end.
Something like:
...
int originalLineSize = width * 3;
int workLineSize = originalLineSize + 4 - originalLineSize % 4;
for (int a = 0; a < bmpArraySize; ++a) {
if ((a % workLineSize) >= originalLineSize)
bmpArray[a] = 0x00;
}
else if (bmpArray[a] < 127) {
bmpArray[a] = 0;
...
}
The "padding bytes" are the bytes following the pixels of a scanline. You are not so interested in the padding as in the scanline size and pixel size:
iScanlineSize = ((width * bitsperpixel) + 31) / 32 * 4;
iBytesperPixel = bitsperpixel / 8;
Now you can loop over scanlines and adress pixels and pixel parts (colors) as follows:
for (int a = 0; a < height; a++) {
for (int b = 0; b < width; b++) {
for (int c = 0; c < iBytesperPixel; c++) {
pixelPart= bmpArray[a*iScanlineSize + b*iBytesperPixel + c];
}
]
}
The color data is just the color data, headers are not included. After I ran my code, the picture with new size shows only black pixel and at the bottom are some random dark colors. The size, width and height are all good but the colors.
//code to change to bigger image
int r, c, i, j;
for (r = 0; r < height; r++)
{
for (c = 0; c < width*3; c++)
{
for (i = 0; i < 2; i++)
{
for (j = 0; j < 2; j++)
{
if (j == 0)
{
bigColorData[2*r+i][c] = oldColorData[r][c];
}
else
{
bigColorData[2*r+i][2*c+2] = oldColorData[r][c];
}
}
}
}
}
From the c < width*3 I deduce you are working with 3 bytes-per-pixel pixels (24 bit color).
But this will not work as the scanlines (width) are rounded up to the nearest word, so the loop must be:
int bits= 3*8;
int scanlinesize= ((bits + 31) / 32 * 4);
unsigned char *pix, *scanline= begin_of_image_data;
for (r = 0; r < height; r++, scanline += scanlinesize)
{
pix= scanline;
for (c = 0; c < width; c++, pix += 3)
{
// now pix points to the first byte of the pixel