问题
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