C++ how to shrink an image using a pointer array?

允我心安 提交于 2019-12-13 08:35:25

问题


I'm writing a c++ program that manipulates images, and this is the function to shrink it. There is a 'pixel' pointer array and a class that has the image colors defined. I cannot use any other library besides the ones included in Visual Studio for this image program. I'm having an issue with this function, I need to traverse the pixels in the image and split it into blocks; the user will enter the block width/height. After the blocks are created, the average RGB values need to be taken from each block (that average will become a single new pixel), and with all of them arranged it will 'shrink' the image. So far it seems the blocks are created because the image becomes smaller, but my image turns completely gray. The totals for the RGB pixels are adding correctly, but something must be off in the rest of the code and I haven't been able to pinpoint it. Here is my code:

//creates a block of average colors based on range of pixels given
pixel CreateBlock(int start, int stop, pixel** currpix, int blockHeight, int blockWidth)
{
    pixel** block;          //Problem? might have to be a single pointer pixel* block[];
    block = new pixel*[blockHeight];

    for (int i = 0; i < blockHeight; i++)
        block[i] = new pixel[blockWidth];

    pixel newPix;

    float totred = 0, totblue = 0, totgreen = 0;
    int redav = 0.0, blueav = 0.0, greenav = 0.0;

    for (int i = 0; i < blockHeight; i++)
    {
        for (int j = 0; j < blockWidth; j++)
        {
            totred = totred + block[i][j].red;
            totblue = totblue + block[i][j].blue;
            totgreen = totgreen + block[i][j].green;
        }
    }

    redav = totred / (blockHeight*blockWidth);
    blueav = totblue / (blockHeight* blockWidth);
    greenav = totgreen / (blockHeight*blockWidth);

    newPix.red = redav;
    newPix.blue = blueav;
    newPix.green = greenav;

    return newPix;
}

//make a new image that is a smaller resampling of the bigger image
void averageRegions(int blockWidth, int blockHeight)
{
    int height = displayed->getHeight(), width = displayed->getWidth();
    int i = 0, j = 0;
    pixel** currpix = displayed->getPixels();           //PROBLEM
    image* shrunk = displayed;
    //shrunk->getPixels();
    shrunk->createNewImage(width / blockWidth, height / blockHeight);
    while (i < height)
    {
        while (j < width)
        {
            int start = i, stop = i + 10;
            shrunk->getPixels()[i][j] = CreateBlock(start, stop, currpix, blockHeight, blockWidth);
            j = j + blockWidth;
        }
        i = i + blockHeight;
    }

    return;
}

Here is the image class:

class image {
    public:
        image();            //the image constructor (initializes everything)
        image(string filename);  //a image constructor that directly loads an image from disk
        ~image();           //the image destructor  (deletes the dynamically created pixel array)

        void createNewImage(int width, int height); //this function deletes any current image data and creates a new blank image
                                                //with the specified width/height and allocates the needed number of pixels
                                                //dynamically.
        bool loadImage(string filename);        //load an image from the specified file path.  Return true if it works, false if it is not a valid image.
                                            //Note that we only accept images of the RGB 8bit colorspace!
        void saveImage(string filename);       //Save an image to the specified path
        pixel** getPixels();                    //return the 2-dimensional pixels array
        int getWidth();                     //return the width of the image
        int getHeight();                    //return the height of the image

        void viewImage(CImage* myImage);  //This function is called by the windows GUI.  It returns the image in format the GUI understands.


    private:
        void pixelsToCImage(CImage* myImage);  //this function is called internally by the image class.
                                            //it converts our pixel struct array to a standard BGR uchar array with word spacing.
                                            //(Don't worry about what this does)
        pixel** pixels;             // pixel data array for image 
        int width, height;      // stores the image dimensions 
};

Here is the pixel class:

class pixel
{
public:
    unsigned char red;      //the red component
    unsigned char green;    //the green component
    unsigned char blue;     //the blue component
};

回答1:


Ok, well minus the massive memory leak and misleading name, you have the right idea in CreateBlock for finding the average. I would try something along the lines of this:

pixel averagePixels(pixel **oldImage, int startRow, int startCol, int blockHeight, int blockWidth){
    float rTot, gTot, bTot;
    pixel avg;

    for(int i = startRow ; i < blockHeight + startRow ; i++){
        for(int j = startCol ; j < blockWidth + startCol ; j++){
            rTot += oldImage[i][j].red;
            gTot += oldImage[i][j].green;
            bTot += oldImage[i][j].blue;
        }
    }
    avg.red   = rTot / (blockHeight * blockWidth);
    avg.green = gTot / (blockHeight * blockWidth);
    avg.blue  = bTot / (blockHeight * blockWidth);
    return avg;
}

pixel **shrinkImage(pixel **oldImage, int blockHeight, int blockWidth){
    int newHeight = oldImage->getHeight() / blockHeight;
    int newWidth  = oldImage->getWidth()  / blockWidth;
    pixel **newImage = new pixel* [newHeight];
    for(int i = 0 ; i < newHeight ; i++)
        newImage[i] = new pixel[newWidth];

    for(int i = 0 ; i < newHeight){
        for(int j = 0 ; j < newWidth){
            newImage[i][j] = averagePixels(oldImage, blockHeight * i, blockWidth * j, blockWidth, blockHeight);
        }
    }
    return newImage;
}

Disclaimer, I haven't actually tested any of this, and it would probably be wise (at least for testing purposes) to ensure that the new rgb values are within the acceptable range (0-255 I presume?). You'll also need some bounds checking/special cases for when the image size is not perfectly divisible by the block size.




回答2:


You are not using the currpix in your CreateBlock code. You are averaging random numbers allocated in the code:

block = new pixel*[blockHeight];
for (int i = 0; i < blockHeight; i++)
    block[i] = new pixel[blockWidth];



回答3:


The issue seems to be that you are using uninitialized values here:

  totred = totred + block[i][j].red;
  totblue = totblue + block[i][j].blue;
  totgreen = totgreen + block[i][j].green;

You allocated block, but you failed to initialize any of the values. Therefore red, green and blue have random values.

You also have a memory leak -- you allocated block here, but failed to deallocate it:

pixel** block; 
block = new pixel*[blockHeight];

for (int i = 0; i < blockHeight; i++)
    block[i] = new pixel[blockWidth];

Instead of this, the easiest solution is to use std::vector<std::vector<pixel>>:

#include <vector>
//...
std::vector<std::vector<pixel> > block(blockHeight, std::vector<pixel>(blockWidth));

That one line of code does everything your loop accomplished, plus rids you of the memory leak. The thing it really doesn't do is initialize block with the values you need to have. What those values are -- you need to determine them.



来源:https://stackoverflow.com/questions/27351737/c-how-to-shrink-an-image-using-a-pointer-array

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!