How to combine two images together

Apr 12, 2009 at 3:45pm
Hi guys, I need assistance in this. I am supposed to make a function:combineImage(const Image & im, int I, int j): that combines two images together, The position of the image to insert is specified such that the upper left pixel of im is located on row i and column j. Note that i and j could be outside of the image we are combing with. Use a blending operation where the combined images overlap. A blending operation averages the overlapping pixel values. Note that each color for each pixel must be averaged separately. Because the combined image must be rectangular we may have empty portions in the final image. The pixels in the empty portions should be black. For example suppose we have an image of size 200 x 300 pixels and we combine this with an image of size 200 x 100 at location i=10 and j=1. The size of the combined image will be 210 x 300 pixels and the combined image will contain empty black regions.


Here is my code if it helps:( I do really appreciate it if u managed to help!)




// Functions for reading and writing a binary ppm image file
// The image is stored in an array of pixels using integers for the colors

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

struct pixel {
int red;
int green;
int blue;
};

class Picture {
private:
pixel *image;
int width, height;

public:
Picture(int h, int w);
Picture( void );
~Picture( void );
void save(string filename);
void load(string filename);
void set(int row, int col, pixel color);
pixel get(int row, int col);
int Width();
int Height();
pixel* pixels();
void flipimage();
void adjustcolors(pixel color);
void reducesize(void);
};


Picture::Picture( void ) {

width = 0;
height = 0;
image = NULL; }

Picture::Picture(int h, int w) {
width = w; height = h;
image = new pixel[height*width];
for (int i=0; i<height; i++)
for (int j=0; j<width; j++) {
image[i*width+j].red = 0;
image[i*width+j].green = 0;
image[i*width+j].blue = 0; }}

Picture::~Picture(void)
{
delete image;
}


void Picture::set(int row, int col, pixel color) {
image[row*width+col] = color; }

pixel Picture::get(int row, int col) {
return image[row*width+col]; }

int Picture::Height(void) {
return height; }

int Picture::Width(void) {
return width; }
pixel* Picture::pixels(void)
{
return image;}



void Picture::load(string imageName){
// read an image from a .ppm file
// returns the image in an array of pixels
// returns the size of the image in width and height

ifstream imageFile;
string s;
char *temp;
int numLevels;

// ios::binary because it is a data file
imageFile.open(imageName.c_str(), ios::binary);

// read in the ppm header
imageFile >> s;
if (s != "P6") {
cout << "Not a valid PPM image" << endl;
}

imageFile >> width >> height >> numLevels;
imageFile.get();

temp = new char[width*height*3];

// read in the image data to temp
imageFile.read(temp,width*height*3);

// copy temp to an array of pixels
image = new pixel[width*height];
for (int i=0; i<width*height; i++) {
unsigned char red = temp[i*3];
unsigned char green = temp[i*3+1];
unsigned char blue = temp[i*3+2];
image[i].red = red;
image[i].green = green;
image[i].blue = blue;
}

delete temp; // return the memory
imageFile.close();
}


void Picture::save(string imageName) {
// store image (array of pixels) in a .ppm file
// widht and height are the size of the input image
ofstream imageFile;
// ios::binary because it is a data file
imageFile.open(imageName.c_str(), ios::binary);

// write the ppm header
imageFile << "P6" << endl << width << endl << height
<< endl << 255 << endl;
char* temp = new char[width*height*3];
// copy the array of pixels in image to temp
for (int i=0; i<width*height; i++) {
char red = image[i].red;
char green = image[i].green;
char blue = image[i].blue;
temp[i*3] = red;
temp[i*3+1] = green;
temp[i*3+2] = blue;
}
// write temp to the file
imageFile.write(temp,width*height*3);

delete temp; // return the memory

imageFile.close();
}


void main (void)
{

string s;
Picture pic1;
pixel color;
cout << "Input image: " ;
cin >> s;
pic1.load(s);


cout << "Output image: " ;
cin >> s;
pic1.save(s);
}


//Then I am supposed to ask the user to input another image to combine it with the existing image
// I really do appreciate it if u managed to help me :)
Last edited on Apr 12, 2009 at 4:33pm
Apr 12, 2009 at 4:43pm
Plz anybody try to help
Apr 12, 2009 at 9:40pm

The hardest part seems to be resizing the image. Another thing that makes this tricky is you have to differentiate between black pixels and "empty" pixels during the blending process -- and currently your 'pixel' struct leaves you no way to determine that.

The easiest (but not necessarily the most optimal) way I can think of to approach this problem is the following:

1) add a bool empty; var to the 'pixel' struct. Set this to 'false' for all pixels when loading the image from a file.

2) make a protected function void copypicture(const Picture& pic,int x,int y); (name it whatever you want, obviously). Since this function is protected you can have it assume that the picture will be big enough to hold the result (ie: x,y are both >= 0, and x+pic.width < width, and y+pic.height < height). This function will do the actual copying blending:

1
2
3
4
5
6
for( ...each pixel being copied... )
{
  if( thispixel.empty )   thispixel = pic.thispixel;
  else                   thispixel = thispixel + pix.thispixel / 2;
  thispixel.empty = false;
}


3) then for your public combineImage() function you can do the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Picture old = *this;  // move 'this' image data to another 'old' object
   // note this means that now both 'this' and 'old' are sharing the same 'image' pointer
   // if they both try to delete it, BAD THINGS happen.
   //   so instead we will re-allocate 'image' here

width = ...calculate width...;
height = ...calculate height...;
image = new pixel[width*height];

// width and height must be large enough to hold the combined image.  I didn't feel like crunching the
//   numbers for this, but if the picture being copied is larger than the 'old' image, then you'll need to increase
//   the size.  Or if the new image is being placed outside the old image.

for(i = 0; i < width*height; ++i)   // zero the image and make it 'empty'
{
  image[i].red = image[i].green = image[i].blue;
  image[i].empty = true;
}

// then copy the old image to this image
copypicture(old, ...whateverx..., ...whatevery...);

// and copy/blend the new picture
copypicture(pic, ...whateverx..., ...whatevery...);

// 'old's dtor will clean it up, and now 'this' is the combined image.
}


I did notice a few problems in your code though. You're using 'char' to read/write pixel data when you probably need unsigned char. And if the file uses unsigned char... why are you using 'int's in your pixel struct?

Also -- this is a big mistake:

If you're using new[] (as you appear to be)... you ***MUST*** use delete[] (which you are not). Ie your destructor is bad:

1
2
3
4
5
Picture::~Picture(void)
{
//delete image;    <--- BAD!  BAD BAD can't emphasise enough
delete[] image;  //  <--- good because you allocated image with new[] --> "image = new pixel[ ... ];"
}

Topic archived. No new replies allowed.