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));
Related
I want to make a grayscale filter so I have to assign the average of all 3 RGB values to each. I use a 2D array because this is a bitmap image and I'm filtering it by changing each pixel.
This is how the struct is defined (where BYTE is uint8_t):
typedef struct
{
BYTE rgbtBlue;
BYTE rgbtGreen;
BYTE rgbtRed;
} __attribute__((__packed__))
RGBTRIPLE;
Do I need to assign the value to each element of struct separately, like I did below, or is there a way to assign that value to all elements of the struct (since they are the same type) in that specific location in the array at once?
This is the code for the filtering function.
void grayscale(int height, int width, RGBTRIPLE image[height][width])
{
for (int i = 0; i < height; i++)
{
for (int j = 0 ; j < width; j++)
{
image[i][j].rgbtBlue = (image[i][j].rgbtBlue + image[i][j].rgbtRed + image[i][j].rgbtGreen)/3;
image[i][j].rgbtGreen = (image[i][j].rgbtBlue + image[i][j].rgbtRed + image[i][j].rgbtGreen)/3;
image[i][j].rgbtRed = (image[i][j].rgbtBlue + image[i][j].rgbtRed + image[i][j].rgbtGreen)/3;
}
}
return;
}
You can do it with a compound literal:
RGBTRIPLE x = image[i][j]; // Use temporary to reduce repetition.
BYTE t = (x.rgbtBlue + x.rgbtRed + x.rgbtGreen)/3;
image[i][j] = (RGBTRIPLE) { t, t, t };
You can certainly avoid making the calculation three times on each loop. Two obvious ways to do this are:
(1) Chain the assignments (note that expressions like a = b = 3; are valid C and the = operator associates right-to-left);
void grayscale(int height, int width, RGBTRIPLE image[height][width])
{
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
image[i][j].rgbtBlue = image[i][j].rgbtGreen = image[i][j].rgbtRed =
(image[i][j].rgbtBlue + image[i][j].rgbtRed + image[i][j].rgbtGreen) / 3;
}
}
return;
}
(2) Calculate that 'gray' value and assign that to the array element (in one go) using a compound literal constructed from that calculated value:
void grayscale(int height, int width, RGBTRIPLE image[height][width])
{
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
BYTE gray = (image[i][j].rgbtBlue + image[i][j].rgbtRed + image[i][j].rgbtGreen) / 3;
image[i][j] = (RGBTRIPLE){ gray, gray, gray };
}
}
return;
}
Whether or not either of the above actually constitutes an improvement is subjective (and a good compiler will likely generate the same code in both cases). However, for more complex situations, these techniques can make your code clearer and its purpose easier for a reader to understand.
Note also that, as pointed out in the comments, the above two examples will assign the same value to each of the three components of the image[i][j] element. In your posted code, the second and third recalculations use one or two values that will have been modified by the previous line(s).
I got this error when I tried to look particular value exist or not. I used a custom data type called RGBTRIPLE that cs50 provides me. If the value is not exist in the memory I will get 'segmentation fault' because I need to find out top left, top middle, top right, value that I have its previous value and next value, bottom left, bottom middle, bottom right value. So for that I used a function called isNull. It will check whether the value exists in the memory or not. If it is not null It will return 1, otherwise 0.
Here is my code
// Blur image
void blur(int height, int width, RGBTRIPLE image[height][width])
{
// entering the array of the image
for (int i = 0; i < height; i++)
{
// entering the array of the row
for (int j = 0; j < width; j++)
{
/* blur: the element we select we need to get the value
of its neighbour value and adding this all including it
then get the avg value of pixel that need to set
*/
int element = 0;
int blueSum = 0;
int greenSum = 0;
int redSum = 0;
RGBTRIPLE pixels[] = {
image[i][j], image[i][j - 1], image[i][j + 1],
image[i - 1][j - 1], image[i - 1][j], image[i - 1][i + 1],
image[i + 1][j - 1], image[i + 1][j], image[i + 1][j + 1]
};
for (int k = 0; k < 9; k++)
{
if (isNull(pixels[k]) == 1)
{
element++;
blueSum += pixels[k].rgbtBlue;
greenSum += pixels[k].rgbtGreen;
redSum += pixels[k].rgbtRed;
}
}
image[i][j].rgbtBlue = round(blueSum / element);
image[i][j].rgbtGreen = round(greenSum / element);
image[i][j].rgbtRed = round(redSum / element);
}
}
return;
}
// check whether it is null or not
int isNull(RGBTRIPLE pixel)
{
if (pixel != 0)
{
return 1;
}
return 0;
}
Error I got:
$ make filter
helpers.c:142:15: error: invalid operands to binary expression ('RGBTRIPLE' and 'int')
if (pixel != 0)
~~~~~ ^ ~
1 error generated.
make: *** [Makefile:2: filter] Error 1
the type RGBTRIPLE is defined in bmp.h as:
/**
* RGBTRIPLE
*
* This structure describes a color consisting of relative intensities of
* red, green, and blue.
*
* Adapted from http://msdn.microsoft.com/en-us/library/aa922590.aspx.
*/
typedef struct
{
BYTE rgbtBlue;
BYTE rgbtGreen;
BYTE rgbtRed;
} __attribute__((__packed__))
RGBTRIPLE;
It is a structure: you cannot compare structures with the == operator, you must compare members individually.
The problem is: what do you mean by check whether it is null or not?
If you mean "is the pixel black?", you should test if all 3 components are 0:
// check whether it is black or not
int isBlack(RGBTRIPLE pixel) {
return ((pixel.rgbtBlue | pixel.rgbtGreen | pixel.rgbtRed) == 0);
}
You get a segmentation fault because you read pixels beyond the boundaries of the matrix image:
the 6th initializer image[i - 1][i + 1] has a typo
you must make special cases for the image boundaries (i == 0, j == 0, i == height - 1 and j == width - 1).
Here is a simple fix:
int min(int a, int b) { return a < b ? a : b; }
int max(int a, int b) { return a < b ? b : a; }
// Blur image
void blur(int height, int width, RGBTRIPLE image[height][width])
{
// entering the array of the image
for (int i = 0; i < height; i++) {
// entering the array of the row
for (int j = 0; j < width; j++) {
/* blur: compute the new color by averaging the components
of all pixels in a 3x3 area around the pixel.
assume that pixel colors continue beyond the image
borders.
*/
unsigned blueSum = 0;
unsigned greenSum = 0;
unsigned redSum = 0;
int i1 = max(0, i - 1);
int i2 = min(height - 1, i + 1);
int j1 = max(0, j - 1);
int j2 = min(width - 1, j + 1);
RGBTRIPLE pixels[] = {
image[i][j1], image[i][j], image[i][j2],
image[i1][j1], image[i1][j], image[i1][j2],
image[i2][j1], image[i2][j], image[i2][j2]
};
for (int k = 0; k < 9; k++) {
blueSum += pixels[k].rgbtBlue;
greenSum += pixels[k].rgbtGreen;
redSum += pixels[k].rgbtRed;
}
image[i][j].rgbtBlue = round(blueSum / 9);
image[i][j].rgbtGreen = round(greenSum / 9);
image[i][j].rgbtRed = round(redSum / 9);
}
}
}
Note however that the above function cannot work as coded because it overwrites the pixel values that will be used for the next column and for the next row. To perform this transformation in place, you can use a 3 line buffer to keep the previous values.
Here is a modified version:
typedef unsigned char BYTE;
typedef struct {
BYTE rgbtBlue;
BYTE rgbtGreen;
BYTE rgbtRed;
} RGBTRIPLE;
// Blur image
void blur(int height, int width, RGBTRIPLE image[height][width])
{
RGBTRIPLE row[3][width + 2];
// populate the initial the row cache
row[1][0] = row[2][0] = image[0][0];
row[1][width + 1] = row[2][width + 1] = image[0][width - 1];
for (int j = 0; j < width; j++) {
row[1][j + 1] = row[2][j + 1] = image[0][j];
}
// entering the array of the image
for (int i = 0; i < height; i++) {
// update the row cache
for (int j = 0; j < width + 2; j++) {
row[0][j] = row[1][j];
row[1][j] = row[2][j];
}
if (i < height - 2) {
row[2][0] = image[i + 1][0];
row[2][width + 1] = image[i + 1][width - 1];
for (int j = 0; j < width; j++)
row[2][j + 1] = image[i + 1][j + 1];
}
// update the image row
for (int j = 0; j < width; j++) {
/* blur: compute the new color by averaging the components
of all pixel values in a 3x3 area around the pixel.
Assume that pixel colors are extended beyond the image
borders.
*/
unsigned blueSum = 0;
unsigned greenSum = 0;
unsigned redSum = 0;
for (int ii = 0; ii < 3; ii++) {
for (int jj = j; jj < j + 3; jj++) {
blueSum += row[ii][jj].rgbtBlue;
greenSum += row[ii][jj].rgbtGreen;
redSum += row[ii][jj].rgbtRed;
}
}
image[i][j].rgbtBlue = (BYTE)((blueSum + 4) / 9);
image[i][j].rgbtGreen = (BYTE)((greenSum + 4) / 9);
image[i][j].rgbtRed = (BYTE)((redSum + 4) / 9);
}
}
}
You cannot compare scalar types with a struct or an array. That is what the error message is telling you.
What condition should be true to make a struct of multiple unrelated bytes to compare with a numerical value?
That's not how it works. You can only compare the fields separately or compare whole struct with another variable of same struct type.
Besides that, you have a few misconceptions in your code.
If the value is not exist in the memory I will get 'segmentation fault'
You got a segmentation fault because the memory you want to read is not your memory. You have no access privileges to read or write.
It will check whether the value exists in the memory or not.
If you provide some value to a function, it will always "exist in memory". You have provided a value in the calling function. How would it not exist?
You cannot detect if the memory location where you copied it from was valid by just looking at the copied value.
Your underlying problem is that you do not verify that you are withing bounds of your array before reading the values.
You must compare row and column index with the limits and only access the array element if you are not out of bounds.
You need to rework your approach to collect pixels to blur.
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'm facing some difficulties while doing the reflect portion of filter. Essentially the struct is
typedef struct
{
BYTE rgbtBlue;
BYTE rgbtGreen;
BYTE rgbtRed;
} __attribute__((__packed__))
RGBTRIPLE;
and I have been attempting to reflect the image by implementing this function.
void reflect(int height, int width, RGBTRIPLE image[height][width])
{
for (int i = 0; i < height; i++)
{
if (width % 2 == 0)
{
for (int j = 0; j < width/2; j++)
{
RGBTRIPLE temp = image[i][j];
image[i][j] = image[i][width - j];
image[i][width - j] = temp;
}
}
else if (width % 3 == 0)
{
for (int j = 0; j < (width - 1)/2; j++)
{
RGBTRIPLE temp = image[i][j];
image[i][j] = image[i][width - j];
image[i][width - j] = temp;
}
}
}
return;
}
The code compiles fine but the end product is the same as the input image. Tried to run debug50 and i figured that my problem lies with the way i swap the RGBTRIPLE values. Any help would be good. Thanks!
What you need to do is to reverse the array.
Why? because you are reflecting image horizontally so you want the left side of the image to become the right side of the image.
Imagine you have this array and you want to reverse it:
int count = 5;
int numbers[count] = {0, 1, 2, 3, 4};
// middle here should be 2.5 but it will be 2 because we cast it to int
int middle = count / 2;
// Reverse array
for (int i = 0; i < middle; i++)
{
// when i is 0, numbers[i] is 0, numbers[count - 1 - i] is 4
temp = numbers[i];
numbers[i] = numbers[count - i - 1];
numbers[count - i - 1] = temp;
}
You should do the same in your function:
// Reflect image horizontally
void reflect(int height, int width, RGBTRIPLE image[height][width])
{
// The middle index
int middle = width / 2;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < middle; j++)
{
// Swap the left side element with right side element
RGBTRIPLE temp = image[i][j];
image[i][j] = image[i][width - j - 1];
image[i][width - j - 1] = temp;
}
}
return;
}
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.