Advertisement

Finding a function to split images

Started by February 15, 2003 05:43 PM
3 comments, last by oriental 22 years ago
This is for MathCAD 2000 so I''m hoping I can get some help here. Ok, I''m going to be as detailed as possible: Imagine a picture that is 512x512. This image is white with black "circles." These "circles" are not filled in; it''s just the perameter of the "circle." They''re not really circles; more like irregular circles. Anyways, these circles are all over the image. The next thing I have to do is divide the picture into 256x256 images. Thus giving four images from the original 512x512. From there I have to write a formula that says whether there is any black in those four images. After it does that, it divides again into 128x128 images, thus giving 16 images of the original 512x512. From there, I have to find any black and how much I have in those images. For example, in the 512x512 there is obviously black in that whole picture. Since there is only one picture (the whole picture itself), that counts as one black. Now in the 256x256, there are four images. For simplicity let''s say each of those four images have black in them. Thus I would say that the 256x256 images have four out of four. In the sixteen 128x128 images, let''s say that only 14 out of the 16 have black in them. Thus I would say the 128x128 images have 14 out of 16 images. This keeps going down to 64x64, 32x32, 16x16, 8x8, 4x4, and finally 2x2 images. I need to find how many blacks there are in each of those images. If you could help me, that would be most appreciated. If you need more info, just write back.
Consider the case where you need to find out how many 1x1 blacks there are in the picture. That is relatively simple, you just code two nested for-loops and use a simple counter to count the squares:

for (y = 0; y<512; y++)for (x = 0; x<512; x++)   if (image[x][y])      blacks++;  


Now go from 1x1 blacks to 2x2 blacks. That code could look like

for (y = 0; y<512; y+=2)for (x = 0; x<512; x+=2)   if (image[x][y] || image[x+1][y] || image[x][y+1] || image[x+1][y+1])      blacks++;  


You can use the same technique to calculate the number of 4x4 blacks, 8x8 blacks, etc., but the if-statements would become rather large then. However, you can use a little trick to make things a lot easier. Suppose you're creating a new image with 2x2 blacks instead of counting the number of 2x2 blacks. If one of the pixels in a 2x2 square is black, then the pixel in the new image is black.

for (y = 0; y<512; y+=2)for (x = 0; x<512; x+=2)   if (image[0][x][y] || image[0][x+1][y] || image[0][x][y+1] || image[0][x+1][y+1])      image[1][x][y] = 1;  


Where image[0][..][..] contains the image, and image[1][..][..] contains the new image (note that the new image is twice as small as the original image, so only image[1][0][0], image[1][2][0], image[1][0][4], etc. contain actual data about the image).

And now for the tricky part: if you want to continue calculating 4x4 blacks, you can use the data of image[1][..][..] to create an image[2][..][..] which consists of 4x4 blacks. And the same thing applies for 8x8 blacks, where you can use the data of image[2] to calculate image[3]. Once you have those images, it's easy to get the number of blacks in the image: just count them as I did in the first code piece.

(hmm, maybe I haven't been that clear so here's some example code in c..)

char image[10][512][512];int imageCreated[10];int power[10];void createBlacksImage(int n){   int x, y;   if (!imageCreated[n])   {      if (!imageCreated[n-1])         createBlacksImage(n-1);            for (y = 0; y<512; y+=power[n])      for (x = 0; x<512; x+=power[n])         if (image[n-1][x][y] || image[n-1][x+power[n-1]][y] || image[n-1][x][y+power[n-1]] || image[n-1][x+power[n-1]][y+power[n-1]])            image[n][x][y] = 1;   }}int getBlacks(n){   int x, y, blacks;   if (!imageCreated[n])      createBlacksImage(n);          for (y = 0; y<512; y+=power[n])   for (x = 0; x<512; x+=power[n])      if (image[n][x][y])         blacks++;}void main(){   int i, n;   n = 1;   for (i = 0; i<10; i++)   {      power  = n;<br>      n *= 2;<br>   }<br><br>   image[0][..] = …;<br><br>   imageCreated[0] = 1;<br><br>   n = 1;<br>   for (int i = 0; i<10; i++)<br>   {<br>      printf("%d x %d: %d blacks\n", n, n, getBlacks(i));<br>      n *= 2;<br>   }<br>}<br>  </pre>  <br><br>Hopefully no bugs.. :-), I haven't tested this code, but it should work. It's kind of memory ineffecient, but there are ways to work around that. Good luck!   <br><br><SPAN CLASS=editedby>[edited by - nickm &#111;n February 16, 2003 2:20:48 PM]</SPAN>    
Advertisement
Personally, I think a recursive solution is easiest here.

(Sorry if this code has a bug somewhere or doesn''t work for some reason; I wrote it quickly right here in response to your post.)


  #include <iostream>using namespace std;int numBlacks(unsigned char ** image, int x, int y, int width, int height, int depthWanted, int currentDepth = 0)//Do not touch the last argument!{    if(width == 1 && height == 1)    {        if(image[x][y] == BLACK)            return 1;        else            return 0;    }    int halfX = width/2,        halfY = height/2        a = numBlacks(image, x,     y,     halfX, halfY, depthWanted, currentDepth+1),        b = numBlacks(image, halfX, y,     halfX, halfY, depthWanted, currentDepth+1),        c = numBlacks(image, x,     halfY, halfX, halfY, depthWanted, currentDepth+1),        d = numBlacks(image, halfX, halfY, halfX, halfY, depthWanted, currentDepth+1);    if(currentDepth <= depthWanted)    {        return (a != 0) + (b != 0) + (c != 0) + (d != 0);    }    else    {        return a + b + c + d;    }}int main(){    cout << numBlacks(image, 0, 0, 512, 512, 3) << endl;    return 0;}  


This code should hopefuly output the number of 64x64 subimages that contain at least one black pixel. For 128x128 images, use a depthWanted of 2. For 32x32 use 4. For 16x16 use 5. For 8x8 use 6. (You should see the pattern by now)

Sorry if this particular piece of code doesn''t work, but you should understand what I mean even if it doesn''t.

Good luck with your project.
As TerranFury suggested, this problem screams recursion. You start with the big picture, the and then divide it into four little pictures. Recurse this for each of the four little pictures, and then each little picture will split itself into little pictures until, at one point, recursion stops. In the previous example, it stops when we reach 1x1 blocks, at which point it begins to unravel and count the black pixels. It adds up the total number of blacks for levels deeper than what we want. If we reach a level where we want to start checking, the logic collapses the sum. However, I am concerned as to whether or not != is gauranteed to evaluate to either 1 or 0. I thought it was just "zero" and "non-zero", or "true" and "false"
In MathCAD you can use the function f(x):=if(x<128,1,0) and vectorize it to convert the grey scale map you loaded with ReadBMP. You can then use a nested for loop with i in 0..floor((rows(x)-1)/2) and j in 0..((cols(x)-1)/2) to loop through the elements. Then use the assignment rslt[i,j] <- (x[i,j] + x[i+1,j] + x[i,j+1] + x[i+1,j+1])/4 to collapse the image to half size. Don''t forget to put rslt on a line by itself at the end to have the function return it instead of the last row/col. That gives you an array of percent black.

The only real alternative is nested arrays, but they are a pain in MathCAD since they are not multi-dimensional arrays. They are 2D arrays within 2D arrays. You would still have the problem of having to make successive calls to get the next higher level.
Keys to success: Ability, ambition and opportunity.

This topic is closed to new replies.

Advertisement