8

Everything went good so far until this pset and the resize exercise, it seems that I'm stuck and need help!

Everything works perfectly when the factor is a whole number (eg. 2). In my code, the factor is declared as a float number. According to ~cs50/hacker4/peek student.bmp staff.bmp, every single line of my headers matches green with the staff ones. The outputted resized picture is identical to the bitmap image created by the ~cs50/hacker4/resize factor infile outfile command.

Headers still match perfectly when I set the factor to a decimal number (eg. 2,6). However, my outputted resized image gets completely messed up while the staff's one looks great.

If it's all about math, I'm wondering why am I crunching numbers right when it comes to set the new values in my outfile's headers if these same values lead to a visual mess once they are represented in my outputted image?

I've read many other topics on this exercise but none of them are talking about how to manage decimal factors. I guess I have to fix a few details but I can't figure out where in my code.

Thanks for your tips!

EDIT #1

I've worked on my code and started to figure out how to downscale a picture but something is still missing.

For more convenience, I've resized the smiley.bmp (8 x 8 pixels) with a factor of 10 to get a bigger image. Then, I've tried to resize my 80 x 80 pixels smiley.bmp with a factor of 0.5

Using staff's code, the smiley is perfectly outputted in a picture of 40 x 40 pixels. In my case, it doesn't as only the top half of the smiley is outputted in a picture of 40 x 40 pixels.

My pseudo looks like this:

//check if factor is less than 1

  //set a float called ratio to original width divided by new width
  //set a float called pixelposition to 0

  //loop through the original height
    //loop through the original width
      //set temporary storage for RGBTRIPLE
      //read each pixels in the current row using temporary storage
      //if abs(pixelposition) == the position of the current pixel
        //write the pixel read in temporary storage
        //add ratio to pixelposition
    //end of width loop
    //reset pixelposition
  //end of height loop

//else

  //...etc

I also outputted a 40 x 40 pixels picture with two smileys drawn next to each others! This happened when I divided the number of elements in fread() by the factor.

To me, it seems that I select the right pixel in the original picture but I write each of them twice (sort of fwrite() / factor) in a picture frame half smaller. I don't figure out to reduce this frequency of writing as when I set a number of elements in fwrite() inferior to 1, it outputs only black pixels.

Last detail: when i change the factor to something more complex (eg. 0,7), it outputs something very messy, a picture with a lot of black pixels. As smiley.bmp only deals with white & red pixels, I guess garbage values are the cause for any other colors outputted. For the moment, I don't know how this happens but I'll have to think about it at some point...

EDIT #2

Here are two parts of my code in pseudo:

if factor less than one
  malloc an array called scanline sizeof RGBTRIPLE * newWidth * abs(oldWith/newWidth)
else
  malloc an array called scanline sizeof RGBTRIPLE * newWidth * factor

Here I malloc an array in which I'll store the pixels I keep in infile before writing them in outfile. To do so:

declare an integer called index (to count my array positions)

iterate over infile's scanlines
  set index to 0
    iterate over pixels in scanlines
      set temporary storage for RGBTRIPLE
      read RGB from infile
      declare a loop of length equals to factor
        scanline[index] = current RGB read
        index++
      close for loop
    stop iterating over pixels
    declare a for loop of length equals to factor
      write the scanline index with a number of elements equals to newWidth
      add new padding
    close for loop
  skip over old padding
stop iterating over scanlines

free scanline

Here I'm sure there's something missing as this piece of code is never told when to keep or dump the pixel read. Same remark for the scanlines. To know which ones must be kept, I've written a very short program, here it is:

#include <stdio.h>
#include <cs50.h>

float width;
float height;
float factor;

int main (void)
{
    printf("Width please: ");
    width = GetInt();
    printf("Height please: ");
    height = GetInt();
    printf("Factor please: ");
    factor = GetFloat();

    float newWidth = abs(width * factor);

    float newHeight = abs(height * factor);

    float pixratioW = width/newWidth;
    float pixratioH = height/newHeight;  
    float pixposW = 0;
    float pixposH = 0;

    //rows to keep
    for (int j = 0; j < newHeight; j++)
    {
        printf("Row %d kept!\n", abs(pixposH));
        pixposH += pixratioH;

        //columns to keep
        for (int i = 0; i < newWidth; i++)
        {
            printf("Column %d kept!\n", abs(pixposW));
            pixposW += pixratioW;
        }
        pixposW = 0;
    }
}

When I transcript this logic to my code, everything goes wrong. I use if conditions to check the pixposW and pixposH and if conditions are met successfully, I fill my scanline array.

Talking about the scanline array, I modify the malloc if factor is inferior to 1:

malloc an array called scanline sizeof RGBTRIPLE * newWidth * pixratioW

Multiplying by 0,xx creates this error:

*** Error in `./resize': free(): invalid next size (fast): 0x08df52d8 ***
Aborted (core dumped)

So to sum everything up: my code written above in pseudo works perfectly when the factor is a whole non number or a decimal number superior to 1. I'm sure I'm close to solve it, I'll soon do a full GDB run to track my code and see when it goes wrong.

1 Answer 1

10

There may be better approaches, but I used an algorithm called nearest neighbor and I wouldn't say my implementation was perfect. I wrote two functions, one for down-scaling and the other for up-scaling.

Down-scaling:

The problem with down-scaling is that even though it's kinda easy to know the dimensions of the resized image, given the dimensions of the original image and a factor, it's a bit tricky to decide which pixels and scanlines are to lose and which ones are to keep.

Horizontal resizing:

For example if the width is 5 pixels and we're resizing with a factor of 0.5, the width of the resized image should be 2 pixels (since we can't have 2.5 pixels).

So we have to pick 2 pixels out of 5. Another way to think about it is that we have to pick 1 pixel out of each 2.5 pixels. Here's a visualization (not drawn at scale):

+++++++++++++++++++++++++
| 0 |  1 |  2 |  3 |  4 |
+++++++++++++++++++++++++
+++++++++++++++++++++++++
|     x     |     y     |
+++++++++++++++++++++++++

So it appears x should be either 0, 1, or 2, and y should be either 2, 3, or 4.

So we each pixel in the resized image might be any one of a sequence of pixels. We can choose each pixel in the resized image to be first pixel in that sequence. So x becomes 0 and y becomes 2 and we drop the rest.

++++++++++
| 0  | 2 |
++++++++++

Here's another example, but this time we'll resize with a factor of 0.8. So the width of a scanline in the resized image will be 5 × 0.8 = 4 pixels out of 5 in the original image. And 5/4 = 1.25, so we're going to pick a pixel out of every 1.25 pixels. Here's a visualization (again, not drawn at scale):

+++++++++++++++++++++++++
| 0 |  1 |  2 |  3 |  4 |
+++++++++++++++++++++++++    
+++++++++++++++++++++++++
| a   |  b  |  c  |  d  |
+++++++++++++++++++++++++

a is going to be one of (0, 1)
b is going to be one of (1, 2)
c is going to be one of (2, 3)
d is going to be one of (3, 4)

So a, b, c, and d are going to be 0, 1, 2, and 3 respectively.

++++++++++++++++++++
| 0 |  1 |  2 |  3 |
++++++++++++++++++++ 

Resizing vertically:

I think one you get the horizontal resizing part, it should become easy to reason about vertical resizing. It's basically the same logic except that you decide on which scanlines to keep and which ones to drop, instead of deciding on individual pixels.


Up-scaling:

The logic is still very close to that of down-scaling. Since we may resize with a floating-point value (>= 1.0), the tricky part is that we need to write certain pixels and scanlines more than the others.

For example, if we're resizing a 5 × 5 image with a factor of 1.5, it should become a 7 × 7 image. So we need to write 2 more pixels in each scanline and 2 more scanlines in the resized image comparing to the original.

The question here is, which pixels and scanlines should we possibly write more than others? And the answer is: we pick them using the same logic above.

Another important question is: how many more times do we write these pixels and scanlines? In this particular example, each pixel and scanline of the ones we picked should be written floor(7/5) = 1 more time.

12
  • Hello Kareem! Thanks for this long and fully detailed reply. I've read and understood the basics of your logic, now I've to work on my code and see if I can implement this two functions myself. I'll keep updating this topic until I've coded the solution.
    – DFATPUNK
    Commented Jun 11, 2015 at 23:05
  • I'm currently working on the down-scaling piece of code I've to implement and when you run the staff resize program, for instance ~cs50/hacker4/resize 0.7 smiley.bmp staff.bmp, it doesn't resize the picture at all, it just cropped the image. In this case, you only get the top left corner of the smiley, the other 3/4 of the face has been thrown away! Staff's resize code surely works perfectly for any factor that up scale the original picture but it doesn't when the factor is set between 0 and 1 exclusive.
    – DFATPUNK
    Commented Jun 12, 2015 at 14:38
  • @DFATPUNK, it's not that the staff's version doesn't work well for factors between 0 and 1. this is probably the simplest down-scaling algorithm, but it's not the best one. other more complex algorithms would probably do more complicated analysis on the image in order to keep the down-scaled version as similar as possible to the original version.
    – kzidane
    Commented Jun 12, 2015 at 14:56
  • OK I get it, I go back to work to see if I can't find out how to fix my code. I'll keep you in touch for sure. Thanks!
    – DFATPUNK
    Commented Jun 12, 2015 at 15:31
  • I've figured out the up-scaling code part, everything works perfect! However, the down-scaling part is still a mystery to me... I've written a short code to calculate which scanlines and pixels to dump. I've got this logic, I've outputted double, half and even a quarter of a 80 x 80 pixels version of smiley.bmp but I've never found how to print out a full smiley in a smaller size. For the upscaling part, I use an array that I fill n times with the RGBTRIPLE I'm reading and I print then the array out n times too. With a float, I can set a factor to a decimal number and resize perfectly.
    – DFATPUNK
    Commented Jun 18, 2015 at 16:32

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .