简体   繁体   中英

C - Parsing arbitrary image file

For a learning example, I'm given an image file to parse that is in this format:

  • Eight bytes denote the height and width as 32 bit ints.
  • The rest of the file is image data filled with pixel information, which are represented by 32 bits of information:
    • 8 bits each for Red,Green,Blue
    • 8 bits for transparency information

I have created a struct, and I can successfully fill it with the data from the binary file.

However I have now been tasked with shrinking/cropping the image. I plan on taking two x,y coordinates. One coordinate to represent the start pixel within the image data, and then another to determine the end pixel such that it will cut a rectangle from the original image and save it into a file (The resulting image will be the rectangle within the 2 sets of coordinates).

What would be an efficient way to do this cropping operation? I was thinking of putting each "pixel" into a pixel struct, and having a 2d array of pixels. However this seems to make it more complicated than may be necessary.

I've searched online, but most examples that relate to image processing, that I found, utilize api's.

The best thing is to have a new struct, think of it as a object. Either load the image from a file or create a new image (filled with zeroes). After that you can use use helper functions which get a pixel or put a pixel to/from an image object.

Internally you can represent the object as a byte array (unsigned char *) or an array of structs, but they need to be defined carefully. For now I'd go with the byte array method. For a struct to work right you MIGHT need to be careful, eg ensure that the size of the struct is the same as what's underneath. With the byte array you'll simply have (y*width+x)*pixel_size as the pointer to the pixel, and +0 for red, +1 for green etc. Since it really sounds like a play project or assignment forget about making it fast, just get it to work and let the compiler worry about the speed.

---- for the transformation ----

for (x=0;x<x_new_max;x++)
for (y=0;y<y_new_max;y++) {
   unsigned old_x = transform_x(x); // essentially old_x = x * scale;
   unsigned old_y = transform_y(y); // essentially old_y = y * scale;
   put_pixel(new_image,x,y,get_pixel(old_image,old_x,old_y));
}

or something similar, the transform function is the key, x=x*scale, scale will depend on the manipulation function, you can perform the transform in float or fixed, if you're good even in integer math. the x scale and y scale might differ.

To shrink image be half new_max_x = old_max_x/2; new_max_y=old_max_y/2; x_scale=0.5; y_scale=0.5;

have fun

It's up to how you have your image class. If you have the pixels stored in a Matrix, then you could do something like:

for (int i = 0; i <= height*shrink_factor; ++i){
     for (int j = y1; j <= width*shrink_factor; ++j){
        new.pixels[i][j] = old.pixels[i/shrink_factor][j/shrink_factor];
     }
}

For the cropping:

for (int i = x1; i <= x2; ++i){
    for(int j = y1; j <= y2; ++j){
        new.pixels[i-x1][j-y1] = old.pixels[i][j];
    }
}

Allocate a block of memory for the new image data. Then set a pointer src to point at the top left pixel of the original image data, and another dst to the start of the new image data. Then just copy one (new) line's worth of pixel data from src to dst for each line of the new image, incrementing src by the original width and dst by the new width after each line.

EDIT: I did an quick implementation of it -- here's the key part of the cropping operation.

The struct I'm using for the image data:

struct image {
  uint32_t    w;      /* width in pixels */
  uint32_t    h;      /* height in pixels */
  uint32_t    *data;  /* pixel data */
};

...And here's the cropping code, where oimg and nimg are pointers to struct image 's for the original image and cropped image respectively. The cropped image's data is allocated (size is nimg->w * nimg->h * sizeof(uint32_t) ) but not initialized. x and y are the top-left coords of the cropped area in oimg . nimg->w and nimg->h have been set to the width and height, in pixels, of the cropped image.

/* src is offset by y lines, plus x pixels into source image data */
uint32_t *src = oimg->data + y * oimg->w + x;
/* dst is at start of new image data */
uint32_t *dst = nimg->data;

for (i = 0; i < nimg->h; i++) {
  /* memcpy() one full new image line (nimg->w * sizeof(uint32_t)) */
  memcpy(dst, src, sizeof(uint32_t) * nimg->w);
  dst += nimg->w;  /* increment dst by a full new image line */
  src += oimg->w;  /* increment src by a full source image line */
}

The above code assumes that there is no extra data between the end of one image line and the start of the next, and that each image has its own pixel data. Some libraries will keep a "stride" value as well as the image width, which holds the offset between lines; this allows for extra padding or unused pixels between image lines, useful for maintaining alignment or to allow images that consist of a part of a larger image with which they share pixel data rather than each having a separate copy.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM