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.
Related
I have been trying to create an edges filter as per CS50 problem set 4. I have seen several solutions, however I would like to know if my approach can work. I'm trying to expand the input image by a black border of one pixel width. For this I want to expand my two-dimensional RGBTRIPLE structure by one pixel on either side. I am setting all values of RGB to 0 (aka black) in the first line and then copy the original image into the temporary structure, substituting all except the border values with the respective colours.
I am defining a variable-length two-dimensional structure RGBTRIPLE which contains three values of the datatype BYTE:
RGBTRIPLE temp[height+2][width+2] = {};
I'm getting the error message that because of the variable length it may not have been initialized, which I understand. I have seen several solutions using pointers and malloc, which I hopefully implemented correctly in the first line. I have been trying to connect the RGBTRIPLE to the pointer as per the following two lines:
RGBTRIPLE *ptr = (RGBTRIPLE *)malloc((height+2)*(width+2)*sizeof(RGBTRIPLE));
RGBTRIPLE temp[height+2][width+2] = &ptr;
temp[height+2][width+2] = {0};
Setting all the values to zero here does also not work, but that's another issue.
I want to use the original RGBTRIPLE in a for-loop and I cannot get this to work. All examples I have seen use the pointers afterwards to add any information. Is there any way to define the RGBTRIPLE using malloc so that I can afterwards use it in code as a "normal" element of the structure as seen with temp[][]:
for(int i = 0; i < height; i++)
{
for(int j = 0; j < width; j++)
{
temp[i+1][j+1] = image[i][j];
}
}
for(int i = 1; i <= height; i++)
{
for(int j = 1; j <= width; j++)
{
int counter = 0;
float gxr, gxb, gxg, gyr, gyb, gyg = 0;
//right pixel
gxb += (2*temp[i][j+1].rgbtBlue);
gxg += (2*temp[i][j+1].rgbtGreen);
gxr += (2*temp[i][j+1].rgbtRed);
etc. for all surrounding pixels.
Any help is appreciated.
You might initialize as in the following sample code.
#include <stdio.h>
#include <stdlib.h>
typedef unsigned char BYTE;
typedef struct tagRGBTRIPLE
{
BYTE rgbtBlue;
BYTE rgbtGreen;
BYTE rgbtRed;
} RGBTRIPLE;
int main()
{
int height = 400;
int width = 600;
RGBTRIPLE img[height][width];
RGBTRIPLE temp[height+2][width+2];
for (int i = 0; i < height; i++) /* Build a sample image file */
{
for (int j = 0; j < width; j++)
{
img[i][j].rgbtRed = 68;
img[i][j].rgbtGreen = 188;
img[i][j].rgbtBlue = 32;
}
}
for (int i = 0; i < (height + 2); i++) /* Initialize the temporary RGBTRIPLE structure*/
for (int j = 0; j < (width + 2); j++)
{
temp[i][j].rgbtRed = 0;
temp[i][j].rgbtGreen = 0;
temp[i][j].rgbtBlue = 0;
}
for(int i = 0; i < height; i++) /* Imported code from the issue */
{
for(int j = 0; j < width; j++)
{
temp[i+1][j+1] = img[i][j];
}
}
for(int i = 0; i <= (height + 2); i++) /* Right and left edges*/
{
float gxr = 0, gxb = 0, gxg = 0;
temp[i][0].rgbtRed = gxr;
temp[i][0].rgbtGreen = gxg;
temp[i][0].rgbtBlue = gxb;
temp[i][width + 1].rgbtRed = gxr;
temp[i][width + 1].rgbtGreen = gxg;
temp[i][width + 1].rgbtBlue = gxb;
}
for(int i = 0; i <= (width + 2); i++) /* Top and bottom edges */
{
float gyr = 0, gyb = 0, gyg = 0;
temp[0][i].rgbtRed = gyr;
temp[0][i].rgbtGreen = gyg;
temp[0][i].rgbtBlue = gyb;
temp[height + 1][i].rgbtRed = gyr;
temp[height + 1][i].rgbtGreen = gyg;
temp[height + 1][i].rgbtBlue = gyb;
}
/* See what we have at a pixel point */
printf("Top edge RGBTRIPLE %d, %d, %d \n", temp[0][144].rgbtRed, temp[0][144].rgbtGreen, temp[0][144].rgbtBlue);
printf("Left edge RGBTRIPLE %d, %d, %d \n", temp[144][0].rgbtRed, temp[144][0].rgbtGreen, temp[144][0].rgbtBlue);
printf("RGBTRIPLE within image %d, %d, %d \n", temp[144][144].rgbtRed, temp[144][144].rgbtGreen, temp[144][144].rgbtBlue);
return 0;
}
C does not really provide a simple way to initialize tuples so you probably would need "for" loops to do this. Experiment with this scenario and see if it applies to the spirit of your project.
Some issues:
Use mem...() where possible.
To zero byte fill an entire variable length array:
// RGBTRIPLE temp[height+2][width+2] = {};
RGBTRIPLE temp[height+2][width+2];
memset(temp, 0, sizeof temp);
I am setting all values of RGB to 0 (aka black) in the first line and then copy the original image into the temporary structure, substituting all except the border values with the respective colours.
Alternative:
// Given image[][] is a 2D array
for(int i = 0; i < height; i++) {
memcpy(temp[i+1], image[i], sizeof image[i]);
}
Initialize properly
float gxr, gxb, gxg, gyr, gyb, gyg = 0; only initializes gyg.
float gxr = 0;
float gxb = 0;
...
float gyg = 0;
Advanced: int math vs. size_t math
int*int*size_t may overflow int*int where size_t*int*int does not.
Cast not needed in C.
Size to the referenced object, not the type.
// RGBTRIPLE *ptr = (RGBTRIPLE *)malloc((height+2)*(width+2)*sizeof(RGBTRIPLE));
RGBTRIPLE *ptr = malloc(sizeof ptr[0] * (height+2) * (width+2));
Program is supposed to take every pixel around the pixel and average color values.
It seems to be working fine visually, but obviously it is not as it doesn't pass the test. The only test it passes is 'pixel in the corner'.
I would really appreciate some hints on where the bug might be.
Here is the code:
//iterate through each row and column
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
/*create variables to define 3x3 box for each pixel
also create variables to store sum of each colour values for all elements of each box*/
int highi = i + 1;
int highj = j + 1;
int counter = 0;
int blue = 0;
int red = 0;
int green = 0;
//iterate through each element of newly created box and add sums of colour values
for(int lowi = i-1 ; lowi <= highi; lowi++)
{
if(lowi < 0)
{
lowi = i;
}
for (int lowj = j-1 ; lowj <= highj; lowj++)
{
if(lowj < 0)
{
lowj = j;
}
blue += image[lowi][lowj].rgbtBlue;
red += image[lowi][lowj].rgbtRed;
green += image[lowi][lowj].rgbtGreen;
counter++;
}
//calculate average of colour values for each pixel
image[i][j].rgbtBlue = blue/counter;
image[i][j].rgbtRed = red/counter;
image[i][j].rgbtGreen = green/counter;
}
The issue was that OP was writing to his image[][] array while reading from it, this causing it to be in error after the first read/write pass.
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 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