Sign in to follow this  

bitmap fun!

This topic is 2586 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey again all!
Im trying to develop a program that recognizes images within other images.
atm i have this function to load the bmp image into a 2d vectorbool GameTexture::Initialize(char * filename)
{
std::cout << "initializing!" << std::endl;
//load up the texture
GameTexture::textureData = LoadBitmapFile(filename, &(GameTexture::textureInfo));

std::cout << "texture width: " << textureInfo.biWidth << std::endl;
std::cout << "texture height: " << textureInfo.biHeight << std::endl;
if(!textureData)
{
std::cout << "ERROR! " << std::endl;
return false;
}

//int counter = 0;
for(unsigned int s=0; s<this->textureInfo.biHeight;s++)
{
std::vector<PixelPart> newVec;
for(unsigned int t=0; t<this->textureInfo.biWidth; t++)
{
PixelPart p;
p.r = (int)this->textureData[(s*textureInfo.biWidth + t)*3];
p.g = (int)this->textureData[(s*textureInfo.biWidth + t)*3 + 1];
p.b = (int)this->textureData[(s*textureInfo.biWidth + t)*3 + 2];

newVec.push_back(p);
}
dataVector.push_back(newVec);
}

//other unnecessary code here
}






I also have this search:


std::cout << "searching for other texture...\n";
if(otherTexture.textureInfo.biHeight > this->textureInfo.biHeight || otherTexture.textureInfo.biWidth > this->textureInfo.biWidth)
{
std::cout << "impossible to search for a texture that is bigger than this one." << std::endl;
return false;
}

bool test = false;
int counter = 0;
int highestMatch = 0;

for(unsigned int s=0; s< this->textureInfo.biHeight; s++)
{
for(unsigned int t=0; t< this->textureInfo.biWidth; t++)
{

if( this->dataVector[s][t].Matches(otherTexture.dataVector[0][0]) )
{
//std::cout << "found starting match: " << s << ", " << t << std::endl;
//loop through the rest of the other texture
int testPosition = 1;
int otherPosY = testPosition / otherTexture.textureInfo.biWidth;
int otherPosX = testPosition % otherTexture.textureInfo.biWidth;

if(s+ otherPosY >= this->textureInfo.biHeight || t+otherPosX >= this->textureInfo.biWidth)
{
break;
}

int whilecounter = 1;
while (this->dataVector[s+otherPosY][t+otherPosX].Matches(otherTexture.dataVector[otherPosY][otherPosX]))
{
//std::cout << "found next match: " << s+otherPosX << ", " << t+otherPosY << std::endl;
whilecounter++;
if(whilecounter > highestMatch)
{
highestMatch = whilecounter;
}
//std::cout << whilecounter << std::endl;
testPosition++;

if(testPosition == otherTexture.textureInfo.biWidth * otherTexture.textureInfo.biHeight)
{
//found a whole match
counter++;
std::cout << "found: " << counter << "starting at pixel: " << s << "," << t << std::endl;
test = true; //found at least one
}

otherPosY = testPosition / otherTexture.textureInfo.biWidth;
otherPosX = testPosition % otherTexture.textureInfo.biWidth;

if(s+ otherPosY >= this->textureInfo.biHeight || t+otherPosX>= this->textureInfo.biWidth)
break;
}

if(s >= this->textureInfo.biHeight || t >= this->textureInfo.biWidth)
break;

}
}
}
if(!test)
{
std::cout << "Couldnt find a single one. highest match:" << highestMatch << std::endl;
}
else
{
std::cout << "done!" <<std::endl;
}

return test;


Unfortunately, i keep recognizing only one row (the first) and it will fail. I believe that my rows are messed up or soemthing. For example, if i take a print screen as one image, cut an image out of that and save it and search for that within the original. The max number of 'matches' it finds is always equal to the width of the image im looking for, so it never matches on the next row.


I did read the bmp spec and reads bits from bottom left to right row by row up to the top right. info can be found here: http://www.fileformat.info/format/bmp/spec/e27073c25463436f8a64fa789c886d9c/view.htm. and yes i am using two 24bit bitmaps


By the way, anybody know of an easy way to copy the code neatly into here without huge tab spacing?
I just changed all the tabs to spaces and it still doesnt do the indentation.

Thanks in advance!
Stu

[Edited by - stu2000 on November 12, 2010 8:02:08 AM]

Share this post


Link to post
Share on other sites
Hi Stu,

When I tested this code, I got slightly different results than you described. The code would segfault after finding a match. This is because the section which recognizes a complete match does not terminate the loop, and continues trying to compare pixels past the end of the smaller otherTexture subbitmap.

if(testPosition == otherTexture.textureInfo.biWidth * otherTexture.textureInfo.biHeight)
{
//found a whole match
counter++;
std::cout << "found: " << counter << "starting at pixel: " << s << "," << t << std::endl;
test = true; //found at least one
break; // <-- You should break the comparison loop here
}

or better yet, return true if you only care about finding one match.


Also, the following test is unnecessary and redundant:

if(s >= this->textureInfo.biHeight || t >= this->textureInfo.biWidth)
break;

because your for loop conditions are already testing this.


As a minor speed improvement, I suggest that you could move the "source texture not big enough" test up to immediately following the first pixel match. In other words, you could change this test:

if(s+ otherPosY >= this->textureInfo.biHeight || t+otherPosX>= this->textureInfo.biWidth)
break;

change it to:

if (s + otherTexture.textureInfo.biHeight > this->textureInfo.biHeight ||
t + otherTexture.textureInfo.biWidth > this->textureInfo.biWidth)
break;

And move it to immediately following the first pixel test:

if( this->dataVector[s][t].Matches(otherTexture.dataVector[0][0]) )

This way, the code doesn't bother attempting a full comparison if the source texture isn't large enough to find a match at those coordinates.


Lastly, it is my understanding that the bitmap spec allows top-to-bottom bitmaps, indicated with a negative biHeight. See http://msdn.microsoft.com/en-us/library/dd183376(v=VS.85).aspx The bottom-to-top bitmaps with a positive biHeight, which you have coded for, are much more common. But you may want to be careful, and check for this in your loading code:

GameTexture::textureData = LoadBitmapFile(filename, &(GameTexture::textureInfo));
if (textureInfo.biBitCount != 24 || // not 24 BPP
textureInfo.biCompression != BI_RGB || // compressed
textureInfo.biHeight < 0) // top-to-bottom
return false;



Here are the suggested changes all together:

GameTexture::Initialize(char * filename)
{
std::cout << "initializing!" << std::endl;
//load up the texture
GameTexture::textureData = LoadBitmapFile(filename, &(GameTexture::textureInfo));

if (textureInfo.biBitCount != 24 || // not 24 BPP
textureInfo.biCompression != BI_RGB || // compressed
textureInfo.biHeight < 0) // top-to-bottom
return false;

std::cout << "texture width: " << textureInfo.biWidth << std::endl;
std::cout << "texture height: " << textureInfo.biHeight << std::endl;
if(!textureData)
{
std::cout << "ERROR! " << std::endl;
return false;
}

//int counter = 0;
for(unsigned int s=0; s<this->textureInfo.biHeight;s++)
{
std::vector<PixelPart> newVec;
for(unsigned int t=0; t<this->textureInfo.biWidth; t++)
{
PixelPart p;
p.r = (int)this->textureData[(s*textureInfo.biWidth + t)*3];
p.g = (int)this->textureData[(s*textureInfo.biWidth + t)*3 + 1];
p.b = (int)this->textureData[(s*textureInfo.biWidth + t)*3 + 2];

newVec.push_back(p);
}
dataVector.push_back(newVec);
}

//other unnecessary code here
}



std::cout << "searching for other texture...\n";
if(otherTexture.textureInfo.biHeight > this->textureInfo.biHeight || otherTexture.textureInfo.biWidth > this->textureInfo.biWidth)
{
std::cout << "impossible to search for a texture that is bigger than this one." << std::endl;
return false;
}

bool test = false;
int counter = 0;
int highestMatch = 0;

for(unsigned int s=0; s< this->textureInfo.biHeight; s++)
{
for(unsigned int t=0; t< this->textureInfo.biWidth; t++)
{

if( this->dataVector[s][t].Matches(otherTexture.dataVector[0][0]) )
{
if (s + otherTexture.textureInfo.biHeight > this->textureInfo.biHeight ||
t + otherTexture.textureInfo.biWidth > this->textureInfo.biWidth)
break;

//std::cout << "found starting match: " << s << ", " << t << std::endl;
//loop through the rest of the other texture
int testPosition = 1;
int otherPosY = testPosition / otherTexture.textureInfo.biWidth;
int otherPosX = testPosition % otherTexture.textureInfo.biWidth;

if(s+ otherPosY >= this->textureInfo.biHeight || t+otherPosX >= this->textureInfo.biWidth)
{
break;
}

int whilecounter = 1;
while (this->dataVector[s+otherPosY][t+otherPosX].Matches(otherTexture.dataVector[otherPosY][otherPosX]))
{
//std::cout << "found next match: " << s+otherPosX << ", " << t+otherPosY << std::endl;
whilecounter++;
if(whilecounter > highestMatch)
{
highestMatch = whilecounter;
}
//std::cout << whilecounter << std::endl;
testPosition++;

if(testPosition == otherTexture.textureInfo.biWidth * otherTexture.textureInfo.biHeight)
{
//found a whole match
counter++;
std::cout << "found: " << counter << "starting at pixel: " << s << "," << t << std::endl;
test = true; //found at least one
break; // <-- You should break the comparison loop here
}

otherPosY = testPosition / otherTexture.textureInfo.biWidth;
otherPosX = testPosition % otherTexture.textureInfo.biWidth;
}
}
}
}
if(!test)
{
std::cout << "Couldnt find a single one. highest match:" << highestMatch << std::endl;
}
else
{
std::cout << "done!" <<std::endl;
}

return test;


You can use the [code] tag to preserve indenting in your forum posts :)

I hope this helps

Share this post


Link to post
Share on other sites
Thank you for your help. I have since found another fault which was my issue. The image i was searching for was not divisible by 4 in width or height, I have since fixed this by making it divisible by 4 and putting the extra pixels as white which are treated as wildcards. Obviously the white pixels should be put at the top rather than the bottom for speed purposes (wildcards at end rather than start).

Thank you.

Share this post


Link to post
Share on other sites

This topic is 2586 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this