4

Using skimage library I have to center images like these. I have found the SimilarityTransform that should accept the offsets but the documentation is lacking. How would I detect the corners of such image and then determine how to center it? The arrays always exist out of 28x28 images. Some images have noise in them like the second image here that represents a four. I accidentally grabbed two pretty centered images but they can be everywhere in the array. At the bottom you can also see more visual representation of the 4.

[[  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0  11 203 229  32   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0  26  47  47  30  95 254 215  13   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0  45 154 185 185 223 253 253 133 175 255 188  19   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0 110 253 253 253 246 161 228 253 253 254  92   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0 128 245 253 158 137  21   0  48 233 253 233   8   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0 139 254 223  25   0   0  36 170 254 244 106   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0  55 212 253 161  11  26 178 253 236 113   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   7 155 253 228  80 223 253 253 109   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0 141 253 253 253 254 253 154  29   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0 110 253 253 253 254 179  38   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   3 171 254 254 254 179   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0 171 253 253 253 253 178   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0  26 123 254 253 203 156 253 200   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0  93 253 254 121  13  93 253 158   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0  64 239 253  76   8  32 219 253 126   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0 133 254 191   0   5 108 234 254 106   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0 132 253 190   5  85 253 236 154   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0 153 253 169 192 253 253  77   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0 112 253 253 254 236 129   9   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0  17 118 243 191 113   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]]

[[  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0  42 164 252  63   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0  23  34   0 244 254 112   0   0   0   0  85   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   4 190 225   0 255 185  13   0   0   0   0  95   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   6 170 254 197  64 254  59   0   0   0   0   0  95   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   9 132 254 204  23 112 254  28   0   0   0   0   0  77   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   6 167 254 216  58  24 242 225  16   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0  76 254 254 162  85 138 254 188   0   0   0  48  85  25   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   3 159 254 254 254 254 254 228 151 151 214 250 254  75   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   7  79 131 158 254 254 226 225 225 225 190 148  39   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0 127 254 148   0   0   0   0   0   0   0  71   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0  23 248 201   0   0   0   0   0   0   0   0  36   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0  85 254 118   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0  12 189 227  22   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0 114 254 103   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0  44 226 175   8   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0 148 203  59   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0  26 242 140   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0 131 169  22   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0  19 233  65   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0 174 109   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]]

enter image description here

SOLUTION CODE: Slightly changed code provided by @anmol_uppal.

def pad_image(img, pad_t, pad_r, pad_b, pad_l):
    """Add padding of zeroes to an image.
    Add padding to an array image.
    :param img:
    :param pad_t:
    :param pad_r:
    :param pad_b:
    :param pad_l:
    """
    height, width = img.shape

    # Adding padding to the left side.
    pad_left = np.zeros((height, pad_l), dtype = np.int)
    img = np.concatenate((pad_left, img), axis = 1)

    # Adding padding to the top.
    pad_up = np.zeros((pad_t, pad_l + width))
    img = np.concatenate((pad_up, img), axis = 0)

    # Adding padding to the right.
    pad_right = np.zeros((height + pad_t, pad_r))
    img = np.concatenate((img, pad_right), axis = 1)

    # Adding padding to the bottom
    pad_bottom = np.zeros((pad_b, pad_l + width + pad_r))
    img = np.concatenate((img, pad_bottom), axis = 0)

    return img

def center_image(img):
    """Return a centered image.
    :param img:
    """
    col_sum = np.where(np.sum(img, axis=0) > 0)
    row_sum = np.where(np.sum(img, axis=1) > 0)
    y1, y2 = row_sum[0][0], row_sum[0][-1]
    x1, x2 = col_sum[0][0], col_sum[0][-1]

    cropped_image = img[y1:y2, x1:x2]

    zero_axis_fill = (images[0].shape[0] - cropped_image.shape[0])
    one_axis_fill = (images[0].shape[1] - cropped_image.shape[1])

    top = zero_axis_fill / 2
    bottom = zero_axis_fill - top
    left = one_axis_fill / 2
    right = one_axis_fill - left

    padded_image = pad_image(cropped_image, top, left, bottom, right)

    return padded_image

# Usage is in a for loop that iterates through 60.000 images.
for item in permuList:
        shuImages.append(center_image(images[item]).reshape((28 * 28)))
Adrian Z.
  • 904
  • 1
  • 12
  • 29
  • 1
    I am interested in this and coincidently was working on same issue, would be glad to help you, Please explain more ? is this the input or expected output ? As the image seems to be centred in the above case. – ZdaR May 28 '15 at 13:40
  • This is the input. I accidentally grabbed a pretty centered image. There are a few thousand more of these and each image is different. – Adrian Z. May 28 '15 at 13:41
  • What is the answer to *detect the corners of such image?* – ZdaR May 28 '15 at 13:44
  • It sounds like you just want to find the center of mass, presumably weighted by the values(?) and then shift it so the center is in the center. To do that with numpy, you'd do i, j = np.indices(x.shape); ctr = (np.sum(i*x)/np.sum(x), np.sum(j*x)/np.sum(x)); scipy.ndimage.shift(x, -ctr) – Ben May 28 '15 at 13:46
  • What I mean by that is that I need to know the corners of each image. In case of this 8 it would be upper left corner is row6 column8, upper right corner is row6 column23, lower left is row25 column8 and lower right is row25 column 23. This is at least something that I think I need for centering with `SimilarityTransform`. – Adrian Z. May 28 '15 at 13:47
  • @Ben each number represents a color. It goes from black to white and so 0 is black and 255 is white. I don't think that what you just said would work for each image (I could be wrong). I'll post an extra example. – Adrian Z. May 28 '15 at 13:50
  • Sounds like you want the center to be the center of the bounding box of the nonzero entries. So instead of the weighted mean in both axes, you'd look at the first and last nonzero index... So take `y = np.max(x, axis=0)` to get the maximum entry in every column. Then np.where(y)[0][0] is the left side of the bbox and np.where(y)[0][1] is the right side. – Ben May 28 '15 at 13:56
  • 1
    Can you please paste the input in comma separated format ? Those spaces are hindering me to convert the data to other type. – ZdaR May 28 '15 at 14:00
  • @Ben what does the axis=0 do? I have seen it a few times but I don't know what it does. – Adrian Z. May 28 '15 at 14:03
  • @anmol_uppal sorry this is how python outputs it. The exact input is a library collection which I can't read with a normal editor. To show you the examples in the questions I have to load the library and print the example, then copy and format it manually to show it as it is right now. – Adrian Z. May 28 '15 at 14:05
  • @Ben if I do what you just said (with the example of 4) it shows me 8 and 9. What can I do with that and what is it? – Adrian Z. May 28 '15 at 14:09
  • @AdrianZ., `axis=0` means "do the operation along the 0th axis". That is, find the maximum of each column. – Ben May 28 '15 at 14:12
  • For your first example, I get (7, 22) and (5, 24) inclusive as the bounding box of the nonzero pixels: `y=where(x.max(axis=0));y[0][0], y[0][-1]` returns (7, 22). – Ben May 28 '15 at 14:16
  • Oh it's -1. Then it returns 8, 23 for the example with the 4 and 7, 22 for the 8. – Adrian Z. May 28 '15 at 14:21
  • 1
    @Ben would that mean that `axis=1` would get the rows? – Adrian Z. May 28 '15 at 14:54
  • @AdrianZ. for the axis clarification you may have a look at: http://www.python-course.eu/numpy.php – ZdaR May 28 '15 at 15:09
  • @anmol_uppal Thanks I'll have a read. – Adrian Z. May 28 '15 at 15:20

1 Answers1

5
img = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 203, 229, 32, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 47, 47, 30, 95, 254, 215, 13, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 154, 185, 185, 223, 253, 253, 133, 175, 255, 188, 19, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 253, 253, 253, 246, 161, 228, 253, 253, 254, 92, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 245, 253, 158, 137, 21, 0, 48, 233, 253, 233, 8, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139, 254, 223, 25, 0, 0, 36, 170, 254, 244, 106, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 212, 253, 161, 11, 26, 178, 253, 236, 113, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 155, 253, 228, 80, 223, 253, 253, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, 253, 253, 253, 254, 253, 154, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 253, 253, 253, 254, 179, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 171, 254, 254, 254, 179, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 171, 253, 253, 253, 253, 178, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 26, 123, 254, 253, 203, 156, 253, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 93, 253, 254, 121, 13, 93, 253, 158, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 64, 239, 253, 76, 8, 32, 219, 253, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 133, 254, 191, 0, 5, 108, 234, 254, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 132, 253, 190, 5, 85, 253, 236, 154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 153, 253, 169, 192, 253, 253, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 112, 253, 253, 254, 236, 129, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 17, 118, 243, 191, 113, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])

So the first part of the question is to find the bounding box in which the image is contained and the second part involves centring the image, For the second part we will add padding to each vertex of the bounding box.

PART 1:

To draw a bounding box, the approach is that we can iterate over each row and find a row with 1 or more non zero elements, doing this procedure both from top and bottom we will get the limits for Y-axis and same we do for X-axis by iterating over columns, Numpy provides some awesome functions such as sum(), we will use this function to check if a row/column has any non zero character, as in that case the sum > 0:

Let (x1, y1) be the top left coordinates and (x2, y2) be the bottom right coordinates, these two points are sufficient to define a bounding box.

col_sum = np.where(np.sum(a, axis = 0)>0)
row_sum = np.where(np.sum(a, axis = 1)>0)
y1, y2 = row_sum[0][0], row_sum[0][-1]
x1, x2 = col_sum[0][0], col_sum[0][-1]
print x1, y1
print x2, y2 

PART 2:

Now we have to centre the image, Rather than calculating the padding required for each side we will just crop the image in the shape of the bounding box and then apply equal padding to each side.

def add_padding(img, pad_l, pad_t, pad_r, pad_b):
    height, width = img.shape
    #Adding padding to the left side.
    pad_left = np.zeros((height, pad_l), dtype = np.int)
    img = np.concatenate((pad_left, img), axis = 1)

    #Adding padding to the top.
    pad_up = np.zeros((pad_t, pad_l + width))
    img = np.concatenate((pad_up, img), axis = 0)

    #Adding padding to the right.
    pad_right = np.zeros((height + pad_t, pad_r))
    img = np.concatenate((img, pad_right), axis = 1)

    #Adding padding to the bottom
    pad_bottom = np.zeros((pad_b, pad_l + width + pad_r))
    img = np.concatenate((img, pad_bottom), axis = 0)

    return img

cropped_image = img[y1:y2, x1:x2]
padded_image = add_padding(cropped_image, 20, 20, 20, 20)
ZdaR
  • 22,343
  • 7
  • 66
  • 87
  • Great! It seems to work fine thanks. I have changed it a bit to keep the 28x28 ratio. When I'm done and everything is in place I'll post the edited code. – Adrian Z. May 28 '15 at 15:20
  • You should be able to simplify this a bit if you use ``np.pad`` and ``skimage.measure.regionprops(image != 0)`` (which returns the bounding box as well as the cut out image). – Stefan van der Walt May 28 '15 at 23:51