My Sepia filter is not working as expected, although in certain cases it does work - c

For my online CS50 course I have to convert an image to Sepia.
I have built this function that should take each pixel and sets the RGB value to Sepia.
The program loads an image via the command prompt and converts its to Sepia.
The programs loads the picture fine, and it also creates a new picture.
Only problem is it is not Sepia. Online I can check my program, here it sometimes calculates the new pixel correct but other times it is wrong. I cannot see any connections on why it works only with some pixels. Probably has to do with the values of the pixel, but I cannot see the input. The formula for sepia was given by Harvard CS50.
Here is a link to the results: https://submit.cs50.io/check50/66b21d4d9453a5e671bb0862bb016a420a73893b
Here is the code that should change the image to Sepia.
// Convert image to sepia
void sepia(int height, int width, RGBTRIPLE image[height][width])
{
for(int i = 0; i < height; i++)
{
for(int j = 0; j < width; j++)
{
//Calculates the sepia value of image[i][j].rgbtRed
int red = round((image[i][j].rgbtRed *.393)+(image[i][j].rgbtGreen *.769)+(image[i][j].rgbtBlue *.189));
//the value can be max 255, if it is higher it should be capped at 255
if (red > 255)
{
red = 255;
}
//Calculates the sepia value of image[i][j].rgbtGreen
int green = round((image[i][j].rgbtRed *.349)+(image[i][j].rgbtGreen *.686)+(image[i][j].rgbtBlue *.168));
if (green > 255)
{
green = 255;
}
//Calculates the sepia value of image[i][j].rgbtblue
int blue = round((image[i][j].rgbtRed *.272)+(image[i][j].rgbtGreen *.534)+(image[i][j].rgbtBlue *.131));
if (blue > 255)
{
blue = 255;
}
//sets the image[i][j] values to the Sepia equavalent
image[i][j].rgbtRed = red;
image[i][j].rgbtGreen = green;
image[i][j].rgbtBlue = blue;
}
return;
}
}

Related

Java: Converting RGB to ARGB to be used in BufferImage setRGB

I'm trying to make a dithering effect to just mess around with in my free time to hopefully learn more about java interfacing. i found out how to convert the 32 bit ARGB int which you get when using the .getRGB(x,y) for a BufferedImage and did the calculations to change the color to the red green and blue values that i would need. Now all I have to do is turn these red green and blue float values into an ARGB int again to be used in the setRGB(x,y,rgb) method. Thanks!
private static void dither(BufferedImage bi) {
int width = bi.getWidth();
int height = bi.getHeight();
for (int y = 0; y < height; y++){
for (int x = 0; x < width; x++) {
int rgb = bi.getRGB(x, y);
float red = (rgb >> 16) & 0x000000FF;
float green = (rgb >>8 ) & 0x000000FF;
float blue = (rgb) & 0x000000FF;
red = Math.round(red / 255) * 255;
green = Math.round(green / 255) * 255;
blue = Math.round(blue / 255) * 255;
bi.setRGB(x, y, rgb); //this is where i need the red, green, blue values to be a single int
}
}
}
I'm thinking maybe I'd have to do the opposite of what i did to get the individual red, green and blue values but I'm not sure.
Please note I am still VERY new to coding and so please feel free to give me feedback on naming conventions and syntax.

Filter program in C does not work on more complex images

I need to write a program which makes a grayscale filter. It works only partially and I am having the following error messages:
:) grayscale correctly filters single pixel with whole number average
:( grayscale correctly filters single pixel without whole number average
expected "28 28 28\n", not "27 27 27\n"
:) grayscale leaves alone pixels that are already gray
:) grayscale correctly filters simple 3x3 image
:( grayscale correctly filters more complex 3x3 image
expected "20 20 20\n50 5...", not "20 20 20\n50 5..."
:( grayscale correctly filters 4x4 image
expected "20 20 20\n50 5...", not "20 20 20\n50 5..."
The code is below:
void grayscale(int height, int width, RGBTRIPLE image[height][width])
{
for(int i = 0; i < height; i++) //Loop thought each row of 2D array
{
for(int j = 0; j < width; j++)//Loop through each pixel of each row
{
int red = image[i][j].rgbtRed;
int blue = image[i][j].rgbtBlue;
int green = image[i][j].rgbtGreen;
int avr = round((red + blue + green) / 3);
image[i][j].rgbtBlue = image[i][j].rgbtGreen = image[i][j].rgbtRed = avr;
}
}
return;
}
round() ineffective as the quotient of two ints is as int. Rounding that
int has no effect.
// ........int........ / int
// int avr = round((red + blue + green) / 3);
// Divide by a `double`
int avr = round((red + blue + green) / 3.0);
// or
int avr = lround((red + blue + green) / 3.0);
// or even better, round with integer math.
int avr = ((red + blue + green)*2 + 3)/6;
Other problems may exist, but this explains "expected "28 28 28\n", not "27 27 27\n".

Cs50's Problem Set 4 - Filter Less - Blur Function

So I have been struggling to correct my code to fix the blur function of pset4 and feel that my code is correct (even if it isn't optimally efficient).
Here's how I went about it:
First I looped through the height and width of the stock image with
2 for loops.
Set a pixelcounter to 0 to count the number of valid pixels around
any particular pixel defined by the j'th pixel of the i'th row.
I set up two for loops to iterate across a 3x3 grid like structure
around the pixel in question. The variables in play here are k and
l. K has to be 1 row above i (hence k = i-1), ending one row below i
(hence k <= i+1) and l has to be 1 pixel behind j (hence l = j-1),
ending 1 pixel ahead of j (hence l <= j+1)
Inside the for loops stated in '3.' I used an 'if loop' to determine
whether the pixel being iterated across in the 3x3 grid exists - by
stating that k has to be greater than -1 and less than 'height' and
ditto for l.
pixelcounter++ to add to the total of valid pixels surrounding the
[i][j]th pixel.
Exiting the messy loop, to ensure all pixels are counted, array
pixelcolour is declared with size of pixelcounter.
I used the same loops as seen in step 3 to loop through the 3x3
pixel grid surrounding the [i][j]th pixel with the same if
condition. Only this time I nested it within a for loop using z as
my pixelcounter, such that it can iterate across the 1D array
pixelcolour, storing in it at the zth position the properties of the
image RGB colours at [k][l] if they are valid.
I declared 3 variables - rawred, rawblue, rawgreen with the purpose
of just totalling the values of red green and blue.
For loop to carry out step 8.
I then initialized average values of RGB components with
avgred/avgblue/avggreen with rawred/rawblue/rawgreen floats divided
by pixelcounter casted as a float. Rounded the result to give
integral values.
I then input those integral values into the pixel at the [i][j]th
pixel.
Here is the code:
void blur(int height, int width, RGBTRIPLE image[height][width])
{
// Looping through height of the image
for (int i = 0; i < height; i++)
{
// Looping through the individual pixels in each row
for (int j = 0; j < width; j++)
{
int pixelcounter = 0;
// Looping through a 3x3 pixel grid surrounding of the individual pixel - Height
for (int k = i - 1; k <= i + 1; k++)
{
// Looping through individual pixels within the kth row
for (int l = j - 1; l <= j + 1; l++)
{
// Counting the number of valid pixels in the 3x3 grid
if ((k > -1) && (k < height) && (l > -1) && (l < width))
{
pixelcounter++;
}
}
}
RGBTRIPLE pixelcolour[pixelcounter];
// Looping through array 3x3 pixel grid surrounding the individual pixel - height
for (int z = 0; z < pixelcounter; z++)
{
for (int k = i - 1; k <= i + 1; k++)
{
for (int l = j - 1; l <= j + 1; l++)
{
// Storing valid pixels in an array of valid pixels
if ((k > -1) && (k < height) && (l > -1) && (l < width))
{
pixelcolour[z] = image[k][l];
}
}
}
}
// adding all RGB components
float rawred = 0;
float rawblue = 0;
float rawgreen = 0;
for (int a = 0; a < pixelcounter; a++)
{
rawred = rawred + pixelcolour[a].rgbtRed;
rawblue = rawblue + pixelcolour[a].rgbtBlue;
rawgreen = rawgreen + pixelcolour[a].rgbtGreen;
}
// Calculating average values of RGB component
int avgred = round(rawred / (float) pixelcounter);
int avgblue = round(rawblue / (float) pixelcounter);
int avggreen = round(rawgreen / (float) pixelcounter);
// Dereferencing original pixel colour to new colour
image[i][j].rgbtRed = avgred;
image[i][j].rgbtBlue = avgblue;
image[i][j].rgbtGreen = avggreen;
}
}
return;
}
P.S: I know there's probably a much more efficient way to do this, but I really want to see where exactly I'm going wrong with this code. It compiles and the end result picture is strangely shifted to the left-corner. Nothing is blurred, the entire picture is shifted by a pixel and there are no pixels that shouldn't have been there (stray pixels with random colours).
EDIT 1:
The following are the errors I am receiving:
:( blur correctly filters middle pixel
expected "127 140 149\n", not "145 160 169\n"
:( blur correctly filters pixel on edge
expected "80 95 105\n", not "90 106 116\n"
:) blur correctly filters pixel in corner
:( blur correctly filters 3x3 image
expected "70 85 95\n80 9...", not "70 85 95\n90 1..."
:( blur correctly filters 4x4 image
expected "70 85 95\n80 9...", not "70 85 95\n90 1..."
Solved the issue that was still bothering me. I realized that the blur function is being affected by surrounding blurred pixels and the solution was to create a copy of the image using a temporary array.

Cs50 sepia problem with converting image from normal to sepia

I'm working on a cs50 program called filter(less comfortable, week 4), and it has to transfer images from normal to sepia. It's working fine unless it has to transfer the color white. When trying to transfer the color white, it just converts it to blue and green. Like so:
ORIGINAL
SEPIA
As you can see, it converted everything fine, except for the white or close-to-white colors.
Here's my code(Sepia part only):
void sepia(int height, int width, RGBTRIPLE image[height][width])
{
for(int j = 0; j < width; j++)
{
for (int i = 0; i < height; i++)
{
int sepiared = image[i][j].rgbtRed *.393 + image[i][j].rgbtGreen *.769 + image[i][j].rgbtBlue *.189;
int sepiagreen = image[i][j].rgbtRed *.349 + image[i][j].rgbtGreen *.686 + image[i][j].rgbtBlue *.168;
int sepiablue = image[i][j].rgbtRed *.272 + image[i][j].rgbtGreen *.534 + image[i][j].rgbtBlue *.131;
image[i][j].rgbtRed = sepiared;
image[i][j].rgbtGreen = sepiagreen;
image[i][j].rgbtBlue = sepiablue;
}
}
return;
}
Please help me understand why this happens. Clang prints no error messages.
Yours truly,
Lost in code:)
First, as an aside, when you have a CS50 question, please search for the keywords in StackOverflow before asking. The answer is probably there. If you search for sepia RGBTRIPLE cs50 you get quite a few hits.
After doing a lot of pixel processing, you'll hone some useful debugging intuitions. Among those:
If you see diagonal offsets, your image's bytes-per-row may be greater than the width times the pixel size. (Especially in YCbCr images or on platforms where image buffers are aligned to 128-bit vector sizes.)
2x or 0.5x image displays probably mean you're not heeding the scale value on a Retina display.
Certain colorspace errors will point you immediately to BGR vs RGB byte ordering issues. No blue channel at all or all blue? Probably mixing ARGB with BGRA.
But more to the point:
If you see wackiness in the bright or color-saturated areas, your pixel component values are probably oversaturating (exceeding the maximum value, and dropping the high bit(s)).
Every time you either (1) multiply a color component by a number great than 1 or (2) add multiple color components together, you need to think about what happens if you exceed the maximum value. If your intermediate math will add two values and then divide by 2, make sure your compiled operations will be using a large enough variable size to hold that extra bit.
So in your inner loop here, when operating on a white pixel, almost every color component will exceed 255 (i.e. red and green will exceed, but not blue, because sepia is low in blue):
int sepiared = image[i][j].rgbtRed *.393 + image[i][j].rgbtGreen *.769 + image[i][j].rgbtBlue *.189;
int sepiagreen = image[i][j].rgbtRed *.349 + image[i][j].rgbtGreen *.686 + image[i][j].rgbtBlue *.168;
int sepiablue = image[i][j].rgbtRed *.272 + image[i][j].rgbtGreen *.534 + image[i][j].rgbtBlue *.131;
The resulting value would be {255, 255, 255} x {.393+.769+.189, .349+.686+.168, .272+.534+.131}, or {344.5, 306.8, 238.9}.
But because you don't have that enough bits for those values in the BYTE components of an RGBTRIPLE struct, your values will be incorrect. So instead, you can do this:
int sepiared = (int) image[i][j].rgbtRed *.393 + image[i][j].rgbtGreen *.769 + image[i][j].rgbtBlue *.189;
int sepiagreen = (int) image[i][j].rgbtRed *.349 + image[i][j].rgbtGreen *.686 + image[i][j].rgbtBlue *.168;
int sepiablue = (int) image[i][j].rgbtRed *.272 + image[i][j].rgbtGreen *.534 + image[i][j].rgbtBlue *.131;
sepiared = min(sepiared, 255);
sepiagreen = min(sepiagreen, 255);
sepiablue = min(sepiablue, 255);
Note that I have made two changes:
Cast the first value in each expression to (int); otherwise the calculation will be done on bytes and you'll lose your high bit.
Enforce the maximum value of 255 on each pixel component.
When considering the other answers, please add my first fix. Checking for a max of 255 won't help if you've already dropped your high bit!
So You Need To Edit Your Code Sligitly
Store Orginal Image To a temp for a while
originalBlue = image[i][j].rgbtBlue;
originalRed = image[i][j].rgbtRed;
originalGreen = image[i][j].rgbtGreen;
the result of each of these formulas may not be an integer so use float and round them to nearest integer
sepiaRed = round(.393 * originalRed + .769 * originalGreen + .189 * originalBlue);
sepiaGreen = round(.349 * originalRed + .686 * originalGreen + .168 * originalBlue);
sepiaBlue = round(.272 * originalRed + .534 * originalGreen + .131 * originalBlue);
if (sepiaRed > 255)
{
sepiaRed = 255;
}
if (sepiaGreen > 255)
{
sepiaGreen = 255;
}
if (sepiaBlue > 255)
{
sepiaBlue = 255;
}
now store the values to the orignal ones
image[i][j].rgbtBlue = sepiaBlue;
image[i][j].rgbtRed = sepiaRed;
image[i][j].rgbtGreen = sepiaGreen;
Declare all Variable Outside For Loop
float sepiaRed;
float sepiaBlue;
float sepiaGreen;
int originalRed;
int originalBlue;
int originalGreen;
I Hope It's Help
You need to use "saturation math".
For near white colors, your intermediate values (e.g. sepiared) can exceed 255.
255 (0xFF) is the maximum value that can fit in an unsigned char
For example, if sepiared were 256 (0x100), when it gets put into rgbtRed, only the rightmost 8 bits will be retained and the value will be truncated to 0. So, instead of a very bright value [near white], you'll end up with a very dark value [near black].
To fix this, add:
if (sepiared > 255)
sepiared = 255;
Also, note with the ordering of your for loops, it is very cache inefficient.
And, it's wasteful [and can be slow] to use image[i][j].whatever everywhere. Better to use a pointer to the current pixel.
Anyway, here's an updated version of your code with these changes:
void
sepia(int height, int width, RGBTRIPLE image[height][width])
{
RGBTRIPLE *pix;
for (int i = 0; i < height; i++) {
pix = &image[i][0];
for (int j = 0; j < width; j++, pix++) {
int sepiared = pix->rgbtRed * .393 +
pix->rgbtGreen * .769 +
pix->rgbtBlue * .189;
int sepiagreen = pix->rgbtRed * .349 +
pix->rgbtGreen * .686 +
pix->rgbtBlue * .168;
int sepiablue = pix->rgbtRed * .272 +
pix->rgbtGreen * .534 +
pix->rgbtBlue * .131;
if (sepiared > 255)
sepiared = 255;
if (sepiagreen > 255)
sepiagreen = 255;
if (sepiablue > 255)
sepiablue = 255;
pix->rgbtRed = sepiared;
pix->rgbtGreen = sepiagreen;
pix->rgbtBlue = sepiablue;
}
}
}
Also, note that it can be a bit slow to use floating point math on pixel images. In this case, it's faster/better to use scaled integer math.
Here's a version that does that:
void
sepia(int height, int width, RGBTRIPLE image[height][width])
{
RGBTRIPLE *pix;
for (int i = 0; i < height; i++) {
pix = &image[i][0];
for (int j = 0; j < width; j++, pix++) {
int sepiared = pix->rgbtRed * 393 +
pix->rgbtGreen * 769 +
pix->rgbtBlue * 189;
int sepiagreen = pix->rgbtRed * 349 +
pix->rgbtGreen * 686 +
pix->rgbtBlue * 168;
int sepiablue = pix->rgbtRed * 272 +
pix->rgbtGreen * 534 +
pix->rgbtBlue * 131;
sepiared /= 1000;
sepiagreen /= 1000;
sepiablue /= 1000;
if (sepiared > 255)
sepiared = 255;
if (sepiagreen > 255)
sepiagreen = 255;
if (sepiablue > 255)
sepiablue = 255;
pix->rgbtRed = sepiared;
pix->rgbtGreen = sepiagreen;
pix->rgbtBlue = sepiablue;
}
}
}
For me it worked when I kept all the variables outside the loop and rounded off in the end after checking the value of each R G B less than 255.

Method to apply Sketch effect and Cartoon effect

I would like to know where I can find 3 good algorithms, or 3 examples of C code to :
apply a sketch effect on an image (black and white)
apply a sketch effect on an image (color)
apply a cartoon effect on an image (color)
I already have a code for the black and white sketch, but it's too white..
void sketchFilter(int *pixels, int width, int height) {
changeImageToGray(pixels, width, height);
int *tempPixels = new int[width * height];
memcpy(tempPixels, pixels, width * height * sizeof(int));
int threshold = 7;
float num = 8;
for (int i = 1; i < height - 1; i++) {
for (int j = 1; j < width - 1; j++) {
Color centerColor(tempPixels[i * width + j]);
int centerGray = centerColor.R();
Color rightBottomColor(tempPixels[(i + 1) * width + j + 1]);
int rightBottomGray = rightBottomColor.R();
if (abs(centerGray - rightBottomGray) >= threshold) {
pixels[i * width + j] = RGB2Color(0, 0, 0); // black
}
else {
pixels[i * width + j] = RGB2Color(255, 255, 255); // white
}
}
}
delete [] tempPixels;
}
Any way to improve this code, or should I go with a completely different?
How can I do both color cartoon (doodle?) and color sketch?
Thanks!
The cartoon-like style is described in this page; it can be achieved via a smoothing followed by a(n aggressive) posterize effect. Both can be carried out using OpenCV, using any filter for the smotthing and PyrMeanShiftFiltering for the posterize.
Edit:
The pencil (colour) sketch is described f.e. in this StackOverflow question.

Resources