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;
}
Related
Got a problem with code in C, the purpose is to blur given image working as a filter. The code reads height and width from RGBTRIPLE bmp.h file, makes a copy of each pixel in advance to compute the average in the middle pixel (when its 3x3 pixels chunk) or the boundary pixel's average (when its 2x3 chunk). I nested for-loops, 2 outer ones to copy each pixel from 'image', defined there 4 integers (3 doubles, 1 int.) to count each pixel's red, green and blue. The last int. is named counter to be my denominator in division.
The problem occurs not in syntax, but on the image. 4 down rows of pixels are like rainbow, each is different, not blurred. And the image is darkened.
When I don't use the pixels' copy it seems to work fine.
// Blur image
void blur(int height, int width, RGBTRIPLE image[height][width])
{
RGBTRIPLE copy[height][width];
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
// make a copy of rgbtriple image
copy[i][j].rgbtRed = image[i][j].rgbtRed;
copy[i][j].rgbtGreen = image[i][j].rgbtGreen;
copy[i][j].rgbtBlue = image[i][j].rgbtBlue;
// i need to ensure that image's pixels wont be out of bounds of rows/columns
// it's inappropriate to have static division by 9 because sometimes there will be less pixels to divide by
double sumRed = 0;
double sumGreen = 0;
double sumBlue = 0;
int count = 0;
for (int ii = i - 1; ii <= i + 1; ii++)
{
for (int jj = j - 1; jj <= j + 1; jj++)
{
if (ii >= 0 && ii < height && jj >= 0 && jj < width)
{
sumRed += copy[ii][jj].rgbtRed;
sumGreen += copy[ii][jj].rgbtGreen;
sumBlue += copy[ii][jj].rgbtBlue;
count++;
}
}
}
if (count != 0 && count <= 9)
{
image[i][j].rgbtRed = round(sumRed / count);
image[i][j].rgbtGreen = round(sumGreen / count);
image[i][j].rgbtBlue = round(sumBlue / count);
}
}
}
return;
}
Thanks in advance!
You compute the new value of the image pixels from the data in the copy matrix, but you did not copy the whole image before hand, only pixel values up to the current pixel. Hence the results are incorrect.
You should copy the whole image in a separate loop or using memcpy.
Here is a modified version:
#include <string.h>
// Blur image
void blur(int height, int width, RGBTRIPLE image[height][width]) {
RGBTRIPLE copy[height][width];
// make a copy of rgbtriple image
#if 1 // using memcpy
memcpy(copy, image, sizeof(copy));
#else
// if you cannot use memcpy
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
copy[i][j] = image[i][j];
}
}
#endif
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
// Mix the color values with the adjacent pixels
// making sure the pixels are inside the image.
// It is inappropriate to always divide by 9
// because depending on the pixel position and image size
// count can be 1, 2, 3, 4, 6 or 9
double sumRed = 0;
double sumGreen = 0;
double sumBlue = 0;
int count = 0;
for (int ii = i - 1; ii <= i + 1; ii++) {
for (int jj = j - 1; jj <= j + 1; jj++) {
if (ii >= 0 && ii < height && jj >= 0 && jj < width) {
sumRed += copy[ii][jj].rgbtRed;
sumGreen += copy[ii][jj].rgbtGreen;
sumBlue += copy[ii][jj].rgbtBlue;
count++;
}
}
}
// no need to test count: there is at least one pixel
image[i][j].rgbtRed = round(sumRed / count);
image[i][j].rgbtGreen = round(sumGreen / count);
image[i][j].rgbtBlue = round(sumBlue / count);
}
}
}
Currently I am doing a computer course in which I have to write a blur filter for .BMG images.
The filter loads the pictures correctly, and creates new ones based on my filter.
I have tested the blur filter using sample pictures, and it seems to work (to my eye).
However the course has an auto grader in which I can see that my results differ.
Here is a link to my results, scroll all the way down to see the results for the blurr function:
https://submit.cs50.io/check50/73d0a0da48b323a1a8e306fc0ef528cd2df269c3
Here is the code that I wrote to solve this problem:
// Blur image
void blur(int height, int width, RGBTRIPLE image[height][width])
{
RGBTRIPLE TEMP[height][width];
for(int i = 0; i < height; i++)
{
for(int j = 0; j < width; j++)
{
TEMP[i][j].rgbtRed = image[i][j].rgbtRed;
TEMP[i][j].rgbtGreen = image[i][j].rgbtGreen;
TEMP[i][j].rgbtBlue = image[i][j].rgbtBlue;
}
}
// this forloop will read in the height
for(int i = 0; i < height; i++)
{
// this forloop will read in the with
for(int j = 0; j < width; j++)
{
float count = 0;
float red = 0;
float green = 0;
float blue= 0;
// this forloop will loop three times, for the average filter
for(int q = -1; q < 2; q++)
{
// this forloop will loop three times, making the total 9 for each pixel
for(int w = -1; w < 2; w++)
{
// this if statement makes sure that the I stay within the pixel I J array
if(w >= 0 && w < width && q >= 0 && q < height)
{
red += TEMP[i+q][j+w].rgbtRed;
green += TEMP[i+q][j+w].rgbtGreen;
blue += TEMP[i+q][j+w].rgbtBlue;
count ++;
}
else
{
continue;
}
}
}
// this should set the [i][j] value of each colour to the average of the values
image[i][j].rgbtRed = round(red/count);
image[i][j].rgbtGreen = round(green/count);
image[i][j].rgbtBlue = round(blue/count);
}
}
}
Hopefully someone can help me with my mistake.
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].
Link to the problem (blur part only) here.
In this code I am trying to blur a bmp image by taking the weighted sum across 3x3 matrix for each pixel and replacing the original value with the average obtained.
I have tried various different things, the code compiles correctly (there is no error), and also I couldn't find any problem while debugging the code , but the output image obtained is not what was desired, it is a black and colored pattern repeated 3 times vertically instead of the edges of the input photo.
Thanks for helping out.
Here is the main code of the function:
// Blur image
void blur(int height, int width, RGBTRIPLE image[height][width])
{
// Array for storing corresponding blur values of the pixels.
RGBTRIPLE(*blur)[width] = calloc(height, width * sizeof(RGBTRIPLE));
if (image == NULL)
{
printf("Not enough memory to store blur pixels.\n");
return;
}
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
sum3x3(row, col, height, width, image, blur);
}
}
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
image[row][col] = blur[row][col];
}
}
free(blur);
return;
}
Here is the code for sum3x3 function:
void sum3x3(int row, int col, int height, int width, RGBTRIPLE matrix[row][col], RGBTRIPLE new_matrix[row][col] )
{
float counter = 0;
float blue = 0;
float green = 0;
float red = 0;
for (int i = -1; i < 2; i++)
{
for (int j = -1; j < 2; j++)
{
// skip rows out of range.
if ( (i + row) < 0 || (i + row >= height)) continue;
// skip columns out of range.
if ( (j + col) < 0 || (j + col >= width)) continue;
// add to sum.
counter ++;
blue += matrix[i + row][j + col].rgbtBlue;
green += matrix[i + row][j + col].rgbtGreen;
red += matrix[i + row][j + col].rgbtRed;
}
}
new_matrix[row][col].rgbtBlue += round(blue/counter);
new_matrix[row][col].rgbtGreen += round(green/counter);
new_matrix[row][col].rgbtRed += round(red/counter);
}
The RGBTRIPLE data structure:
typedef struct
{
BYTE rgbtBlue;
BYTE rgbtGreen;
BYTE rgbtRed;
} __attribute__((__packed__))
RGBTRIPLE;
Update: I made some changes to the code according to the suggestions in comments but still not getting the desired output.
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