I am trying to pass a pointer rgb that is initialized with memset to 0 and then looped through to place a 32 bit integer only in the bounds that I create with height and width input (h and w) as well as offset from the top left corner of the 2d array (x and y). after compiling, I seem to have the value with printf of the pointer just after it was made which gives the correct value (in my case 0xFFFFFF with and input of 255 255 255 for r g b) but after it is passed through to rgb2yuv function, it is set to 0 when I printf there.
Any suggestions would be awesome!
#include <stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<errno.h>
#include<string.h>
/*global definitions*/
#define WIDTH 480
#define HEIGHT 240
/*global declarations*/
int w,h,scrn, bytewrite;
unsigned char red, green, blue;
static unsigned long rgb[WIDTH][HEIGHT];
/*function declarations*/
void colorq();
void rgb_rectdraw(int x, int y, int w, int h, unsigned char red ,
unsigned char green, unsigned char blue, unsigned long *rgb);
void rgb2yuv(unsigned long *rgb);
/*
Function Name: main
Purpose: main function
*/
int main(int argc, char** argv){
printf("\n");
int x, y;
/*call colorq to create a 32bit number of RGB*/
colorq();
/ *call rgb_rectdraw to draw a rectangle RGB array*/
rgb_rectdraw(x, y, w, h, red, green, blue, rgb);
/*call rgb2yuv to take the RGB array and covert it to a YUV array*/
rgb2yuv(rgb);
return 0;
}
/*
Function name: color q
Purpose: asks user to input colors from 0 to 255 in RGB format
*/
void colorq(){
printf("Please enter a color for Red Green and Blue from 0 to 255:\n");
scanf("%hu", &red);
scanf("%hu", &green);
scanf("%hu", &blue);
printf("\n");
return;
}
/*
Function name: rectdraw
Purpose: Draws a rectangle array
*/
void rgb_rectdraw(int x, int y, int w, int h,unsigned char red,
unsigned char green, unsigned char blue,unsigned long *rgb){
unsigned long rgbpixel;
/* testing only take out when
finished debugging why red is always 0 after scanf */
red = 255;
printf("red set to 255 for debugging\n");
/*creates a 32-bit number of RGB*/
rgbpixel = (red<<16)|(green<<8)|blue;
printf("%#x\n",rgbpixel);
/*create array of width w height h*/
/*initialize array*/
memset (rgb, 0,sizeof(HEIGHT*WIDTH));
int i, j, startx, stopx, starty, stopy;
printf("enter width and height of rectangle in pixels\n");
scanf("%d %d", &w, &h);
printf("enter offset x pixels and y pixels of rectangle:\n");
scanf("%d %d", &x, &y);
startx=x;
starty=y;
stopx=x+w;
stopy=y+w;
/* creates array of w and h of int rgh */
for(i=startx; i <= stopx; i++){
for(j=starty; j <= stopy; j++){
rgb = rgbpixel;
}
j = 0;
}
printf("original rgb %#x\n",rgb);
return ;
}
/*
*Function Name: rgb2yuv
*Purpose: convert the RGB array to a YUV array
*/
void rgb2yuv(unsigned long *rgb){
int i,j;
printf("ptrpassed = %#x\n",*rgb);
for(i=0; i<=WIDTH;i++){
for(j=0; j<=HEIGHT; j++){
}
}
printf("\n");
return;
}
I went through and basically sorted out all the warts, and explained why. A lot of it amounts to the fact that if your compiler spits out warnings, you have to listen to them.
/* Changed: Code formatted for my sanity */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
/*global definitions*/
#define WIDTH 480
#define HEIGHT 240
/*global declarations*/
int w,h,scrn, bytewrite;
/* Changed: to a short because I don't like the fact that I might be overwriting
memory on accident (in colorq). */
unsigned short red, green, blue;
static unsigned long rgb[WIDTH][HEIGHT];
/* Changed: Some format strings have been changed to get rid of compiler
warnings. */
/*function declarations*/
void colorq();
/* Changed: Specify the second dimension when you're passing an array. */
void rgb_rectdraw(int x, int y, int w, int h, unsigned char red,
unsigned char green, unsigned char blue,
unsigned long rgb[][HEIGHT]);
/* Changed: always pass an array of arrays. */
void rgb2yuv(unsigned long rgb[][HEIGHT]);
/*
Function Name: main
Purpose: main function
*/
int main(int argc, char** argv)
{
printf("\n");
int x, y;
/*call colorq to create a 32bit number of RGB*/
colorq();
/* call rgb_rectdraw to draw a rectangle RGB array */
rgb_rectdraw(x, y, w, h, red, green, blue, rgb);
/* call rgb2yuv to take the RGB array and covert it to a YUV array */
rgb2yuv(rgb);
return 0;
}
/*
Function name: color q
Purpose: asks user to input colors from 0 to 255 in RGB format
*/
void colorq(){
/* Suggestion: restructure this method to just take in all its input
locally, then return a rgbpixel. */
printf("Please enter a color for Red Green and Blue from 0 to 255:\n");
scanf("%hu", &red);
scanf("%hu", &green);
scanf("%hu", &blue);
printf("\n");
return;
}
/*
Function name: rectdraw
Purpose: Draws a rectangle array
*/
void rgb_rectdraw(int x, int y, int w, int h,unsigned char red,
unsigned char green, unsigned char blue,
unsigned long rgb[][HEIGHT])
{
unsigned long rgbpixel;
/* testing only take out when
finished debugging why red is always 0 after scanf */
red = 255;
printf("red set to 255 for debugging\n");
/*creates a 32-bit number of RGB*/
/* Changed: Added the extra 0xFF masking because of shortness rather than
charness. */
rgbpixel = ((red & 0xFF) << 16) | ((green & 0xFF) << 8) | (blue & 0xFF);
printf("%#lx\n",rgbpixel);
/*create array of width w height h*/
/*initialize array*/
/* Changed: fill the size of one element times the number of elements */
memset(rgb, 0, sizeof(unsigned long) * HEIGHT * WIDTH);
int i, j, startx, stopx, starty, stopy;
printf("enter width and height of rectangle in pixels\n");
scanf("%d %d", &w, &h);
printf("enter offset x pixels and y pixels of rectangle:\n");
scanf("%d %d", &x, &y);
startx=x;
starty=y;
stopx=x+w;
stopy=y+w;
/* creates array of w and h of int rgh */
for(i=startx; i <= stopx; i++){
for(j=starty; j <= stopy; j++){
rgb[i][j] = rgbpixel;
}
j = 0;
}
printf("original rgb %#lx\n", (long unsigned int) rgb);
return ;
}
/*
*Function Name: rgb2yuv
*Purpose: convert the RGB array to a YUV array
*/
void rgb2yuv(unsigned long rgb[][HEIGHT]){
int i,j;
/* Changed: You can't just dereference rgb twice -- you have to use array
notation here. */
printf("ptrpassed = %#lx\n", rgb[0][0]);
for(i=0; i<=WIDTH; i++){
for(j=0; j<=HEIGHT; j++){
}
}
printf("\n");
return;
}
This actually has a number of bugs, but your first issue is assigning the pixel value to the array:
for(i=startx; i <= stopx; i++){
for(j=starty; j <= stopy; j++){
rgb = rgbpixel;
}
j = 0;
}
You probably meant something like this:
rgb[i][j] = rgbpixel;
You don't need to reset j to 0 -- the inner for-loop will immediately reset j to starty anyhow.
Also, you're misusing sizeof(). You probably want sizeof(rgb) instead.
Why not use a structure? If the code has no intention of being portable you could easily get away with:
struct rgb_bits {
int red : 8;
int green : 8;
int blue : 8;
};
union rgb {
rgb_bits colour;
long array;
}
Blanking out the pixel then simply becomes:
union rgb pixel;
pixel.array = 0;
and setting individual colours becomes:
union rgb pixel;
pixel.colour.red = ...;
pixel.colour.green = ...;
pixel.colour.blue = ...;
Looks like you're storing your single pixel value in the pointer to your data to me:
rgb = rgbpixel;
Related
I have written some serial code which I would like to optimise as much as possible before I parallelise it using OpenMP. The program reads in a PPM file by iterating through the pixel data in 4x4 cells (variable c), then it finds the average RGB value of each of those 4x4 cells, and then finally writes to a new file by outputing the average colour value, again of each of the 4x4 cells. This creates a sort of mosaic/pixelation effect.
Having performance profiled my code, the main bottlenecks are fscanf and fprintf. I am ignoring execution time to read/write to disk, so these two functions do not matter.
My effort to optimise so far:
Loop jamming: There are two nested FOR loops within the code which have the exact same loop conditions. However, the second set of nested FOR loops requires that the functions needed to calculate the average RGB value are kept outside of that specific set. The average RGB value calculations are then needed for use in the third set of nested FOR loops (which have the same loop conditions as the second set). Because of this, I have struggled to combine the second and third sets of nested FOR loops despite their similarity.
Loop invariant computations: I have tried to move certain operations outside of the loop where possible, but this has proven to be difficult.
To summarise: How can I optimise this program to reduce the execution time as much as possible?
My code:
typedef struct { //struct holding RGB type int
int r, g, b; //12 bytes
} pixelInt;
typedef struct { //struct holding RGB type unsigned char
unsigned char r, g, b; //3 bytes
} pixel;
int c = 4; // Variable of 4x4 grids
int width, height; //image variable declarations
//Raw 1 dimensional store of pixel data - will contain all the data for each pixel in the image
pixel *data = (pixel *)calloc(width * height, sizeof(pixelInt));
//Loop through entire input image
for (int yy = 0; yy < height; yy += c)
{
for (int xx = 0; xx < width; xx += c)
{
//the total colour of cell of size 'c'
pixelInt cell_tot = { 0, 0, 0 }; //zero initialize struct containing mosaic cell pixel totals.
unsigned int counter = 0; //the counter for how many pixels are in a 4x4 cell
int bx = xx + c; //used in loop conditions
int by = yy + c;
// Store each color from the cell into cell_tot struct
for (int y = yy; y < by; y++)
{
for (int x = xx; x < bx; x++)
{
unsigned int index_1d = x + y * width; //calculate 1d index from x-index (x), y-index(y) and width;
unsigned char r, g, b; //maximum vales are 255, i.e. unsigned char data type
fscanf(f, "%hhu %hhu %hhu", &r, &g, &b); //%hhu is unsigned char specifier
//store the pixel value into data array
data[index_1d].r = r;
data[index_1d].g = g;
data[index_1d].b = b;
counter++; //increment counter
//average pixel color of cell
cell_tot.r += r;
cell_tot.g += g;
cell_tot.b += b;
}
}
//average colour of cell found by dividing cell total by loop counter
pixel cell_average;
cell_average.r = cell_tot.r / counter;
cell_average.g = cell_tot.g / counter;
cell_average.b = cell_tot.b / counter;
//Loop through the new image in cells of size c
for (int y = yy; y < by; y++)
{
for (int x = xx; x < bx; x++)
{
unsigned int index_1d = x + y * width; //calculate 1d index from x-index (x), y-index(y) and width;
//Assign average cell value to the pixels in the cell
data[index_1d].r = cell_average.r;
data[index_1d].g = cell_average.g;
data[index_1d].b = cell_average.b;
//Output the average colour value for the image
fprintf(f_output, "%hhu %hhu %hhu \t", data[index_1d].r, data[index_1d].g, data[index_1d].b);
}
fprintf(f_output, "\n"); //Prints new line
}
}
}
On a 1024x1024 image on my machine your code executes in 0.325s. The following code executes in 0.182s:
unsigned w = width/c, h = height/c;
unsigned *accum = (unsigned*)malloc(3*sizeof(unsigned)*w);
char *line = (char*)malloc(12*w);
unsigned denom = c*c;
//Loop through entire input image
for (int yy = 0; yy < h; ++yy)
{
memset(accum, 0, 3*sizeof(unsigned)*w);
// read and accumulate c lines
for(int y = 0; y < c; ++y)
{
for (int xx = 0; xx < w; ++xx)
{
for (int x = 0; x < c; ++x)
{
unsigned char r, g, b;
fscanf(f, "%hhu %hhu %hhu", &r, &g, &b);
accum[3*xx+0] += r;
accum[3*xx+1] += g;
accum[3*xx+2] += b;
}
}
}
// format a line
for(int xx = 0; xx < w; ++xx)
{
char *cell = line + 12*c*xx;
snprintf(cell, 12, "%3u%4u%4u", accum[3*xx]/denom, accum[3*xx+1]/denom, accum[3*xx+2]/denom);
cell[11] = '\t';
for(int x = 1; x < c; ++x)
memcpy(cell + 12*x, cell, 12);
}
// write it out times c
line[12*w-1] = '\n';
for(int y = 0; y < c; ++y)
fwrite(line, 12*w, 1, f_output);
}
The trick here is to format the averaged values only once and then duplicate the formatted strings. Also by acting on one row at a time I have better chances of utilizing the memory caches.
To go beyond that you would need to re-implement the fscanf to parse the integers faster.
I have two codes: vector_add.c and vector_print.c. Both require me to assign the vector length N, as a macro using #define N (3), where 3 is the length. I am okay with this for vector_add.c, but I wan't to generalize vector_print.c to work for any vector length, and then just assign the vector length later in another code. I tried using #undef N in different ways but I couldn't get it to work.
Does anyone have any suggestion for how I can generalize my vector_print function to work for any vector length, N? Right now, I'm simply using #define N (3) for both files, which works fine for a vector of length 3. But I don't want to explicitly assign a vector length in vector_print.c. I want it to work for any N!
This is vector_add.c:
#include <vector_print.h>
#include <stdio.h>
// vector length (fixed)
#define N (3)
// ----------------------------------------------------------------------
// vector_add
//
// returns the linear sum of the two vectors and puts them in another
// x: first vector
// y: second vector
// z: solution vector
void
vector_add(double *x, double *y, double *z)
{
for (int i = 0; i < N; i++) {
z[i] += x[i] + y[i];
}
}
// ----------------------------------------------------------------------
// main
//
// test the vector_add() function
int
main(int argc, char **argv)
{
double x[N] = { 1., 2., 3. };
double y[N] = { 2., 3., 4. };
double z[N] = {0.,0.,0.};
vector_add(x, y, z);
printf("x is ");
vector_print(x);
printf("y is ");
vector_print(y);
printf("z is ");
vector_print(z);
return 0;
}
This is is vector_print.c:
#include <stdio.h>
// vector length (fixed)
#define N (3)
// ----------------------------------------------------------------------
// vector_print
//
// prints the elements of an N-element array
// name: name of vector to print
void
vector_print(double *name)
{
for (int i = 0; i < N; i++) {
printf("%g ", name[i]);
}
printf("\n");
}
And if necessary, here is vector_print.h:
#ifndef VECTOR_PRINT_H
#define VECTOR_PRINT_H
void vector_print(double *name);
#endif
Just relaying the solution in the comments.
What you can do is pass the length to those functions like this:
void vector_print(double *name, int len);
void vector_add(double *x, double *y, double *z, int len);
This will allow you to control the loop like this:
for (int i = 0; i < len; i++) { // Note: i < len now, not N
printf("%g ", name[i]);
}
So basically I have a struct Pixel:
struct Pixel {
int r;
int g;
int b;
} Pixel;
To store RGB values from a file like this:
0
240
233
2
234
42
Where each 3 values is the red, green and blue value respectively.
Now i've created an array of fixed width and height (I already know the image width and height), so here is the code I have so far:
#define WIDTH 640
#define HEIGHT 480
//new array of WIDTH rows, HEIGHT columns
struct Pixel *rgbArray[WIDTH][HEIGHT];
int x, y;
for(y = 0; y < HEIGHT; y++) {
for(x = 0; x < WIDTH; x++) {
struct Pixel *newPixel;
fscanf(fd, "%d\n%d\n%d\n", &newPixel->r, &newPixel->g, &newPixel->b);
rgbArray[x][y] = newPixel;
}
}
It crashes without error, can anyone help me figure out why? I hope it's not something simply stupid ;_;.
Thanks in advance
First problem
struct Pixel *newPixel;
is uninitialized and dereferencing it with the inderiction operator -> is undefined behavior which might explain your crash, you don't seem to need a pointer so
struct Pixel newPixel;
should be fine, and then
if (fscanf(fd, "%d%d%d", &newPixel.r, &newPixel.g, &newPixel.b) == 3)
rgbArray[x][y] = newPixel;
else
handle_error();
provided that
struct Pixel rgbArray[WIDTH][HEIGHT];
Ok, so I have code that will read in the header from a ppm image, allocate memory for the size of the image, and will successfully print the blank spots (a random number repeated in terminal) for each pixel. All I need to know is how I am supposed to read in the red green and blue values (the 3 separate values ranging from 0-255) for each pixel. I don't know how to access this data within each pixel. Here is my code so far:
#include <stdio.h>
#include <stdlib.h>
int subscript(int row,int column,int numberColumns);
int sub(int rd, int gr, int bl);
//table of contents for the subscript
int main(void){
char header[5];
scanf("%s",header);
printf("%s",header);
printf("\n");
int width, height, depth;
scanf("%d %d %d\n", &width, &height, &depth);
printf("%d %d %d\n", width, height, depth);
int red = 0;
int green = 0;
int blue = 0;
int sm;
int r = 0;
int c = 0;
//allocate memory for bit to be used throughout main
unsigned char *bit = malloc(width*height);
int *rgb = malloc(3*depth);
//loops to read in table and print it
while(r < height){
while(c < width)
{
int col;
scanf("%d",&col);
//brings in allocated memory and values
bit[subscript(r,c,width)] = col;
int clr;
rgb[sub(red,green,blue)] = clr;
int color = clr + col;
printf(" %d",clr);
c=c+1;
}
printf("\n");
r = r + 1;
c = 0;
}
free(bit);
}
int subscript(int row,int column, int numberColumns)
{
return row * numberColumns + column;
//retuns items for subscript
}
int sub(int rd, int gr, int bl)
{
return rd+gr+bl;
}
PPM files store colours as triples of bytes (one byte each for r, g, and b in that order) if the maximum colour value field in the header is less than 256 and triples of two bytes if it is 256 or greater (so two bytes for each of r, g, and b, again in that order).
In the two bytes per channel case, the bytes are MSB first (big-endian).
scanf("%c", &byte_var); will read you a single character (byte) into byte_var, which should be an appropriate type. You can then process it accordingly. scanf("%hhu", &byte_var); will read an unsigned character if you are using C99 - if you are using C89 you should look up getchar.
Now, scanf returns the number of successfully converted arguments or EOF, so you should be checking the result for both incorrect input and the end of input.
For example:
int n;
char c;
while ((n = scanf("%c", &c)) != EOF && 1 == n) {
// do something
}
An example use of this would be:
// Read width, height, work out how many bytes you need per pixel.
...
// Now read some pixels
// Here I assume one byte per pixel; this would be a good situation
// to use a function for reading different channel widths
w = 0, h = 0;
while ((n = scanf("%hhu%hhu%hhu", &r, &g, &b)) != EOF && 3 == n
&& w < width && h < height) {
// now do something with r, g, b
// e.g. store them in an appropriate pixels array at pixels[w][h]
...
++w;
if (w == width) {
w = 0, ++h;
}
}
I also see you are reading from stdin, which I find a bit unusual since the title indicates you are working with a file. You can open a file with fopen (don't forget to check the result), use fscanf to read from it, and use fclose to close it afterwards.
However, I suggest reading the whole file into memory and processing it there, because reading a file a few bytes at a time is slow.
Last, I note that you are only allocating width * height bytes for bit when you need bytes_per_channel * num_channels * width * height, not checking whether your malloc failed, not freeing rgb. You should fix these things.
I am trying to flip an image in C vertically so if the image is < it will end up > and my function includes
//Setting the struct up for the pixel's
struct pixel
{
unsigned char red;
unsigned char green;
unsigned char blue;
};
//Setting the struct up for the Image Type and scanning in the pxiels into an array
struct ImageType
{
char ppImage[3];
char comment[256];
char newlinechar;
int width, height;
int maxColor = 255;
struct pixel image[100][100];
};
//Function in order to flip the image, going from the left most pixel flipping with the right most
void MirrorVertical(struct ImageType imgur)
{
int x,y;
const int middle = imgur.width / 2;
struct pixel tmp;
struct *pixel p;
for(y=0; y < imgur.height; ++y)
{
p = tmp + y * imgur.width;
for(x=0; x < middle; ++x)
{
tmp = p[x];
p[x] = p[imgur.width - 1 - x];
p[imgur.width - 1 - x] = tmp;
}
}
}
I got my structs to work but for some reason my function will not output it, I am scanning in the image into from a struct so....
//Scanning in the pixels for the first image
for(i=imageA.height-1; i <= 0; i--)
{
for(j=0; j < imageA.width; j++)
{
scanf("%hhu", &imageA.image[i][j].red);
scanf("%hhu", &imageA.image[i][j].green);
scanf("%hhu", &imageA.image[i][j].blue);
}
}
What am I doing wrong in my function?
It should be
for(x=0; x < width; x++)
{
for(y = 0; y < height/2; y++)
{
temp = imgur.image[x][y];
imgur.image[x][y] = imgur.image[x][height-y-1]
imgur.image[x][height-y-1] = temp;
}
}
}
Shouldn't this: for(i=imageA.height-1; i <= 0; i--) be for(i=imageA.height-1; i >= 0; i--)? (in the "scanning in the pixels for the first image" code)
I imagine your compiler must be complaining about
struct pixel tmp;
struct *pixel p;
for(y=0; y < imgur.height; ++y)
{
p = tmp + y * imgur.width;
You are adding a struct to an int and allocating the result to a pointer. How is it supposed to work?
EDIT now that you have updated your question with "better" code and it's still not working, here are a few things you could / should change.
You declare a variable tmp then try to access temp. Recipe for failure
You pass the entire struct imgur to the function. That means "make a copy of everything". You should really pass a pointer to the object - change the prototype to reflect that, and access the elements as imgur->height etc
You never declare the variables height and width in your MirrorVertical function
(minor) you compute the value height - 1 - y twice per inner loop - 20000 times in total. If you swap the inner and outer loops and compute it just once (and assign to a new variable newY) you can save a little bit of time (not sure it it's really more efficient since you end up looping over X which might destroy cache coherence instead, especially with big images).
My compiler (and the C standard) complains about the statement int maxColor = 256; in the definition of the struct; you cannot initialize a value in the typedef.
Miscellaneous other errors thrown by the compiler.
I took the liberty of fixing many of them - that leads to the following code which appears to compile and run; now all you need is add your "input image" and "output image" functions (maybe).
#include <stdio.h>
//Setting the struct up for the pixels
struct pixel
{
unsigned char red;
unsigned char green;
unsigned char blue;
};
//Setting the struct up for the Image Type and scanning in the pixels into an array
struct ImageType
{
char ppImage[3];
char comment[256];
char newlinechar;
int width;
int height;
int maxColor; // cannot initialize this here; removed "=256"
struct pixel image[100][100];
};
//Function in order to flip the image, going from the left most pixel flipping with the right most
void MirrorVertical(struct ImageType *imgur) // using a pointer to the struct
{
int x,y, height, width; // added declaration of height, width
// const int middle = imgur->width / 2; // removed, not used
struct pixel tmp; // use same name here and in loop
height = imgur->height; // initialize once - save a redirect later
width = imgur->width; // ditto
for(y = 0; y < imgur->height/2; y++) // made this the outer loop
{
int newY = height - y - 1; // so we only compute it once
for(x=0; x < imgur->width; x++)
{
tmp = imgur->image[x][y]; // use "tmp" not "temp"
imgur->image[x][y] = imgur->image[x][newY];
imgur->image[x][newY] = tmp;
}
}
}
// a simple main program… this doesn't really do anything except call the function
int main(void) {
struct ImageType i1;
// … need to add code to import the image
MirrorVertical(&i1); // note - passing POINTER to i1, not the entire struct
// … need to add code to export the image
}
Let me know if that works.