Scaling up an image using nearest-neighbor - c

I have been trying to make my program scale up an image. I had some problem to allocate new space for my scaled image, but I think it is fixed. The problem I am having is that the program crashes when I am trying to send back my image from my temporary memory holder.
The loaded image is placed in my struct Image. The pixels are placed in
img->pixels, the height in img->height and the width in img->width. But I have no idea why the program crashes when I transfer the pixels from my tmp2 struct to my img struct while it does not crash when I do the opposite. Here is the code:
void makeBigger(Image *img, int scale) {
Image *tmp2;
tmp2 = (Image*)malloc(sizeof(Image));
tmp2->height = img->height*scale;
tmp2->width = img->width*scale;
tmp2->pixels = (Pixel**)malloc(sizeof(Pixel*)*tmp2->height);
for (unsigned int i = 0; i < img->height; i++)
{
tmp2->pixels[i] = (Pixel*)malloc(sizeof(Pixel)*tmp2->width);
for (unsigned int j = 0; j < img->width; j++)
{
tmp2->pixels[i][j] = img->pixels[i][j];
}
}
free(img->pixels);
//scaling up the struct's height and width
img->height *= scale;
img->width *= scale;
img->pixels = (Pixel**)malloc(sizeof(Pixel*)*img->height);
for (unsigned int i = 0; i < tmp2->height; i++)
{
img->pixels[i] = (Pixel*)malloc(sizeof(Pixel)*img->width);
for (unsigned int j = 0; j < tmp2->width; j++)
{
img->pixels[i][j] = tmp2->pixels[i+i/2][j+j/2];
}
}
}
I would be glad if you have any idea of how to make the nearest-neighbor method to work.
EDIT: I am trying to crop the inner rectangle so I can scale it up (zoom).
Image *tmp = (Image*)malloc(sizeof(Image));
tmp->height = img->height / 2;
tmp->width = img->width / 2;
tmp->pixels = (Pixel**)malloc(sizeof(Pixel*) * tmp->height);
for (unsigned i = img->height / 4 - 1; i < img->height - img->height / 4; i++) {
tmp->pixels[i] = (Pixel*)malloc(sizeof(Pixel) * tmp->width);
for (unsigned j = img->width / 4; j < img->width - img->width / 4; j++) {
tmp->pixels[i][j] = img->pixels[i][j];
}
}
for (unsigned i = 0; i < img->height; i++) {
free(img->pixels[i]);
}
free(img->pixels);
img->height = tmp->height;
img->width = tmp->width;
img->pixels = tmp->pixels;
free(tmp);

I see that you're overcomplicating things (walking over the image twice for example).Here's the code (I am posting the whole program - I made assumptions about Pixel and Image that might not match what you have), but if you copy / paste makeBigger it should work in your code OOTB:
code00.c:
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
typedef uint32_t Pixel;
typedef struct {
uint32_t width, height;
Pixel **pixels;
} Image;
void makeBigger(Image *img, int scale)
{
uint32_t i = 0, j = 0;
Image *tmp = (Image*)malloc(sizeof(Image));
tmp->height = img->height * scale;
tmp->width = img->width * scale;
tmp->pixels = (Pixel**)malloc(sizeof(Pixel*) * tmp->height);
for (i = 0; i < tmp->height; i++) {
tmp->pixels[i] = (Pixel*)malloc(sizeof(Pixel) * tmp->width);
for (j = 0; j < tmp->width; j++) {
tmp->pixels[i][j] = img->pixels[i / scale][j / scale];
}
}
for (i = 0; i < img->height; i++)
free(img->pixels[i]);
free(img->pixels);
img->width = tmp->width;
img->height = tmp->height;
img->pixels = tmp->pixels;
free(tmp);
}
void printImage(Image *img)
{
printf("Width: %d, Height: %d\n", img->width, img->height);
for (uint32_t i = 0; i < img->height; i++) {
for (uint32_t j = 0; j < img->width; j++)
printf("%3d", img->pixels[i][j]);
printf("\n");
}
printf("\n");
}
int main()
{
uint32_t i = 0, j = 0, k = 1;
Image img;
// Initialize the image
img.height = 2;
img.width = 3;
img.pixels = (Pixel**)malloc(sizeof(Pixel*) * img.height);
for (i = 0; i < img.height; i++) {
img.pixels[i] = (Pixel*)malloc(sizeof(Pixel) * img.width);
for (j = 0; j < img.width; j++)
img.pixels[i][j] = k++;
}
printImage(&img);
makeBigger(&img, 2);
printImage(&img);
// Destroy the image
for (i = 0; i < img.height; i++)
free(img.pixels[i]);
free(img.pixels);
printf("\nDone.\n");
return 0;
}
Notes (makeBigger related - designed to replace the content of the image given as argument):
Construct a temporary image that will be the enlarged one
Only traverse the temporary image once (populate its pixels as we allocate them); to maintain scaling to the original image and make sure that the appropriate pixel is "copied" into the new one, simply divide the indexes by the scaling factor: tmp->pixels[i][j] = img->pixels[i / scale][j / scale]
Deallocate the original image content: since each pixel row is malloced, it should also be freed (free(img->pixels); alone will yield memory leaks)
Store the temporary image content (into the original one) and then deallocate it
Output:
[cfati#cfati-5510-0:/cygdrive/e/Work/Dev/StackOverflow/q041861274]> ~/sopr.sh
### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###
[064bit prompt]> ls
code00.c
[064bit prompt]> gcc -o code00.exe code00.c
[064bit prompt]> ./code00.exe
Width: 3, Height: 2
1 2 3
4 5 6
Width: 6, Height: 4
1 1 2 2 3 3
1 1 2 2 3 3
4 4 5 5 6 6
4 4 5 5 6 6
Done.

Related

Super-sampling anti-aliasing for a curve via libpng

I tried to smoothen a line via the super-sampling anti-aliasing technique by adding transparency to neighboring pixels. Here is the code in C
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <png.h>
#define WIDTH 512
#define HEIGHT 512
int main()
{
// Image buffer
unsigned char image[HEIGHT][WIDTH][4];
for (int i = 0; i < HEIGHT; i++)
{
for (int j = 0; j < WIDTH; j++)
{
image[i][j][0] = 255;
image[i][j][1] = 255;
image[i][j][2] = 255;
image[i][j][3] = 0;
}
}
// A sample curve
for (double x = -M_PI; x <= M_PI; x += M_PI / WIDTH)
{
int y = (int)(HEIGHT / 2 - sin(x) * cos(x) * HEIGHT / 2);
int i = (int)(x * WIDTH / (2 * M_PI) + WIDTH / 2);
// The anti-aliasing part
int sample = 2; // how far we are sampling
double max_distance = sqrt(sample * sample + sample * sample);
for (int ii = -sample; ii <= sample; ii++)
{
for (int jj = -sample; jj <= sample; jj++)
{
int iii = i + ii;
int jjj = y + jj;
if (iii >= 0 && iii < WIDTH && jjj >= 0 && jjj < HEIGHT)
{
// Here is my question
int alpha = 255 - (int)(100.0 * sqrt(ii * ii + jj * jj) / max_distance);
image[jjj][iii][0] = 0;
image[jjj][iii][1] = 0;
image[jjj][iii][2] = 0;
image[jjj][iii][3] = alpha > image[jjj][iii][3] ? alpha : image[jjj][iii][3];
}
}
}
}
FILE *fp = fopen("curve.png", "wb");
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop info = png_create_info_struct(png);
png_init_io(png, fp);
png_set_IHDR(png, info, WIDTH, HEIGHT, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
png_write_info(png, info);
for (int i = 0; i < HEIGHT; i++)
{
png_write_row(png, (png_bytep)image[i]);
}
png_write_end(png, NULL);
fclose(fp);
return 0;
}
Although it does some smoothing, but the result is far from the available programs. Where did I do wrong?
I tried to calculate the transparency based on the distance of each neighboring pixel from the center of the line:
int alpha = 255 - (int)(100.0 * sqrt(ii * ii + jj * jj) / max_distance);
I used the factor of 100 instead of 255 since we do not need to go deep into full transparency.
The problem is how to set the alpha value for each pixel based on neighboring pixels when the transparency of each neighbor is subject to change according to its neighbors?

C issue, blur filter not working properly

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

error: 8.13802e+06 is outside the range of representable values of type 'unsigned char'

close to the finish I've encountered a problem I can't solve. Maybe one of you can:
The compiling of the following code works fine but when I fire the program I get this error message:
helpers.c:228:42: runtime error: 8.13802e+06 is outside the range of representable values of type 'unsigned char'
The code is a function to blur an image blockwise, but the very first pixel [0][0] does not get a correct average value and I don't know why I get that error message instead.
// Blur image
void blur(int height, int width, RGBTRIPLE image[height][width])
{
int i;
int j;
int m;
int n;
int averageRed;
int averageBlue;
int averageGreen;
RGBTRIPLE average[height][width];
// For each row of the image...
for (i = 0; i < height; i++)
{
//...take each pixel.
for (j = 0; j < width; j++)
//If current height equals 0 AND current width equals 0..
if (i == 0 && j == 0)
{
//..take 2 rows of the picture..
for (m = i; m <= i + 1; m++)
{
//..and take 2 pixels of each row.
for (n = j; n <= j + 1; n++)
{
//Sum up the rgb-values for each of the 2 pixel of the 2 rows.
averageRed = averageRed + image[m][n].rgbtRed;
averageGreen = averageGreen + image[m][n].rgbtGreen;
-> The error line averageBlue = averageBlue + image[m][n].rgbtBlue;
}
}
//Save the average of the values in a separate array after the 2x2 pixel-block
average[i][j].rgbtRed = round((float)averageRed / 4);
average[i][j].rgbtGreen = round((float)averageGreen / 4);
average[i][j].rgbtBlue = round((float)averageBlue / 4);
//Set average-variables to 0
averageRed = 0;
averageGreen = 0;
averageBlue = 0;
}
//From each row of the image...
for (i = 0; i < height; i++)
{
//...take each pixel..
for (j = 0; j < width; j++)
{
//...and update the original value with the temporary stored value.
image[i][j].rgbtRed = average[i][j].rgbtRed;
image[i][j].rgbtGreen = average[i][j].rgbtGreen;
image[i][j].rgbtBlue = average[i][j].rgbtBlue;
}
}
}
Thanks in advance for any hint!
Another facepalm-answer :-)..
The solution was quite simple. It was just the missing initialization of the averageRed/Green/Blue-variables.
Didn't get it because the error message pointed only to the averageBlue.
Thanks to M Oehm again :-)

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.

Proper way to display PNG using libpng onto a graphics buffer

I am trying to load png image using LibPNG c library onto a graphics buffer like so
//m_pBuffer is the graphics buffer
//row_pointers is the data from png
//width is width of image
//height height of image
//y = 100
//x = 100
//ScreenWidth = 1024
//channel_num = 4
int pDst = (y + height - 1) * ScreenWidth + x;
for (y = height - 1; y >= 0 ; y--)
{
png_bytep row = row_pointers[y];
u32 *pBuffer = m_pBuffer + pDst;
for(x = 0; x < width; x++) {
png_bytep px = &(row[x * channel_num]);
*pBuffer++ = RGBA(px[3], px[2], px[1], px[0]);
}
pDst -= ScreenWidth;
}
but what I get is a slightly distorted image.
Yet this is the original pic
How do I get rid of the slightly yellow background and white borders?
This is how row_pointers is populated
png_bytep row_pointers[height];
int row = 0;
for (row = 0; row < height; row++)
row_pointers[row] = NULL;
for (row = 0; row < height; row++)
row_pointers[row] = (png_byte*)png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr));
int pass = 0;
int number_passes = 1;
for (pass = 0; pass < number_passes; pass++)
{
for (int j = 0; j < height; j++)
{
png_read_rows(png_ptr, NULL, &row_pointers[j], 1);
}
}
png_read_end(png_ptr, info_ptr);

Resources