Most efficient way to expand 2D array, with dimension (n x m) to a new array of dimension (n + w)(m + w), while keeping contents centered - c

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!

Related

pset4 blur Segmentation fault

I'm stuck on my code for too much time now and needing some help. I'm working on CS50 pset4 blur filter and I keep getting either a "Segmentation fault" or "Floating point exception" depending if try to change my " neighbour variables" on float instead of int. Can someone have any idea what I'm doing wrong with that ?
// Blur image
void blur(int height, int width, RGBTRIPLE image[height][width])
{
// Copy image to "copy"
RGBTRIPLE copy[height][width];
for(int i = 0; i < height; i++)
{
for(int j = 0; j < width; j++)
{
copy[i][j] = image[i][j];
}
}
// Loop through all the neighbour's pixel and calculate the average RGB in each
int RedNeighbour = 0; int BlueNeighbour = 0; int GreenNeighbour = 0; int neighbourSum = 0;
for(int i = 0; i < height; i++)
{
for(int j = 0; j < width; j++)
{
for(int k = i - 1; k < i + 1; k++)
{
for(int l = j - 1 ; l < j + 1; l++)
{
if(k >= 0 && k < height && l >= 0 && l < width)
{
RedNeighbour += copy[l][k].rgbtRed;
BlueNeighbour += copy[l][k].rgbtBlue;
GreenNeighbour += copy[l][k].rgbtGreen;
neighbourSum++;
}
else
{
continue;
}
}
}
// Divide each color by the sum of the neighbouring pixels and copy the pixe into original image
image[i][j].rgbtRed = round(fmin(255, RedNeighbour/neighbourSum));
image[i][j].rgbtBlue = round(fmin(255, BlueNeighbour/neighbourSum));
image[i][j].rgbtGreen = round(fmin(255, GreenNeighbour/neighbourSum));
}
}
return;
}
Thanks !
I'd recommend just using a debugger (gdb, or your IDE's if you're using one) for that kind of thing.
That said, I'm noticing that on the following lines, you are potentially accessing out of bound indices in your copy array:
RedNeighbour += copy[l][k].rgbtRed;
BlueNeighbour += copy[l][k].rgbtBlue;
GreenNeighbour += copy[l][k].rgbtGreen;
In your code, l is constrained by your width, while k is constrained by your height. However, the definition of the copy array is RGBTRIPLE copy[height][width];, which means that you should probably be accessing your copy array with copy[k][l] rather than copy[l][k].

Dynamically allocate 2-dimensional structure array in C

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

Blur function for cs50 PSET4, where are the errors?

I have been stuck with this function for days now. I looked at other people questions, I watched youtube tutorials videos, but I can't get it right.
The task is part of Harvard's CS50 course (https://cs50.harvard.edu/x/2020/psets/4/filter/less/).
Any kind of help would be much appreciated! I really don't want to go on with the course without understanding what the problem is.
//check if pixels are valid
bool valid_pixel(int r, int c, int height, int width)
{
return r >= 0 && c >= 0 && r < height && c < width;
}
// Blur image
void blur(int height, int width, RGBTRIPLE image[height][width])
{
//create a copy of the original image
RGBTRIPLE temp[height][width];
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
temp[i][j] = image[i][j];
}
}
int red, green, blue, count;
red = green = blue = count = 0;
//iterate through rows
for (int i = 0; i < height; i++)
{
//iterate through columns
for (int j = 0; j < width; j++)
{
//move one pixel up to one pixel down in the rows
for (int r = i - 1; r <= i + 1; r++)
{
//move one pixel left to one pixel right in the columns
for (int c = j - 1; c <= j + 1; c++)
{
//check if they are unvalid pixels
if (valid_pixel(r, c, height, width))
{
//count every valid pixel
count ++;
//"store" every pixel color
red += image[r][c].rgbtRed;
green += image[r][c].rgbtGreen;
blue += image[r][c].rgbtBlue;
}
}
}
//calculate average values
temp[i][j].rgbtRed = round((float)red / count);
temp[i][j].rgbtGreen = round((float)green / count);
temp[i][j].rgbtBlue = round((float)blue / count);
}
}
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
image[i][j] = temp[i][j];
}
}
return;
The primary problem is that you forget to reset red, blue and green variables (that you sum up) after each outer loop iteration. You should put this line inside the main loop-
red = green = blue = count = 0;
Also, you're copying the image into another temporary image and copying that temporary image into the original image again in the end. This is very in-efficient. You should not copy the pixels from the original image into the blurred image at first. You can put the modified values directly into this temporary image. And in the end, use memmove to efficiently move entire rows to the original image at once. (Remember to #include <string.h>)
void blur(int height, int width, RGBTRIPLE image[height][width])
{
RGBTRIPLE blurred_img[height][width];
//iterate through rows
for (int i = 0; i < height; i++)
{
//iterate through columns
for (int j = 0, red, green, blue, count; j < width; j++)
{
// Reset the variables
red = blue = green = count = 0;
//move one pixel up to one pixel down in the rows
for (int r = i - 1; r <= i + 1; r++)
{
//move one pixel left to one pixel right in the columns
for (int c = j - 1; c <= j + 1; c++)
{
//check if they are unvalid pixels
if (valid_pixel(r, c, height, width))
{
//count every valid pixel
count++;
//"store" every pixel color
red += image[r][c].rgbtRed;
green += image[r][c].rgbtGreen;
blue += image[r][c].rgbtBlue;
}
}
}
//calculate average values
blurred_img[i][j].rgbtRed = round((float)red / count);
blurred_img[i][j].rgbtGreen = round((float)green / count);
blurred_img[i][j].rgbtBlue = round((float)blue / count);
}
}
for (int i = 0; i < height; i++)
{
// Copy the new image over to the original, row by row
memmove(image[i], blurred_img[i], sizeof(RGBTRIPLE) * width);
}
return;
}
This is assuming valid_pixel is correct. To determine whether the pixel position is valid you can just do-
if (k > 0 && k < height && l > -1 && l < width))
Notice however, when r (the row) is invalid, the innermost loop (column loop) is still iterated until c == width even though the entirety of this loop is useless, because r is invalid and it'll stay that way until the innermost loop completes and r increments.
For efficiency, you should break whenever r is invalid-
if (r < 0 || r > height - 1)
{
break;
}
if (c > -1 && c < width)
{
//count every valid pixel
count++;
//"store" every pixel color
red += image[r][c].rgbtRed;
green += image[r][c].rgbtGreen;
blue += image[r][c].rgbtBlue;
}

Reflecting (flipping) an image horizontally in C using 'temp' array

I am trying to reflect(filp horizontally) an image and I can't quite seem figure out why the image does not reflect and stays in original condition. I've tried many different methods (adding -1 to [width - j] or assigning the tempArray as RGBTRIPLE tempArray;and dividing width with /2 as the loop condition.
No errors being thrown, and I've followed the solutions from Stackoverflow and it seems like I am not quite getting there, any advices?
// Reflect image horizontally
void reflect(int height, int width, RGBTRIPLE image[height][width])
{
RGBTRIPLE tempArray[height][width];
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
tempArray[i][j] = image[i][j];
image[i][j] = image[i][width - j];
image[i][width - j] = tempArray[i][j];
}
}
return;
}
As mentioned in the comments, you have 2 issues. First, since you iterate over the entire row, you end up swapping each item 2x. And that leaves just the original array.
Second, you access one past the end of the array with image[i][width - j] when j == 0. Valid indexes for an array are 0...(length-1).
Another, minor issue is that you don't need an array for the temp value.
// Reflect image horizontally
void reflect(int height, int width, int image[height][width])
{
int temp;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width / 2; j++)
{
temp = image[i][j];
image[i][j] = image[i][width - j - 1];
image[i][width - j - 1] = temp;
}
}
return;
}
Tested: https://ideone.com/EAALtI

How I can resize a bitmap properly?

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.

Resources