nuno-faria/tiler

How do I count the number of each color block used in each picture?

contilend opened this issue · 7 comments

How do I count the number of each color block used in each picture?

You can make the place_tile function return a boolean that states if a tile was placed in the image, like this:

def place_tile(img, box):
    p1 = np.flip(box['pos'])
    p2 = p1 + box['img'].shape[:2]
    img_box = img[p1[0]:p2[0], p1[1]:p2[1]]
    mask = box['tile'][:, :, 3] != 0
    mask = mask[:img_box.shape[0], :img_box.shape[1]]
    if OVERLAP_TILES or not np.any(img_box[mask]):
        img_box[mask] = box['tile'][:img_box.shape[0], :img_box.shape[1], :][mask]
+      if len(img_box[mask]) > 0:
+           return True
+   return False

Then you can count the number of tiles in the create_tiled_image function:

def create_tiled_image(boxes, res, render=False):
    print('Creating tiled image')
    img = np.zeros(shape=(res[0], res[1], 4), dtype=np.uint8)
+   n_tiles_placed = 0

    for box in tqdm(sorted(boxes, key=lambda x: x['min_dist'], reverse=OVERLAP_TILES)):
        result = place_tile(img, box)
        if render:
            show_image(img, wait=False)
            sleep(0.025)
+       if result:
+           n_tiles_placed += 1
    
+   print(f'{n_tiles_placed} tiles placed')

    return img

I think I've got it. To find the frequency of the colors used you can add the following code:

def place_tile(img, box):
    p1 = np.flip(box['pos'])
    p2 = p1 + box['img'].shape[:2]
    img_box = img[p1[0]:p2[0], p1[1]:p2[1]]
    mask = box['tile'][:, :, 3] != 0
    mask = mask[:img_box.shape[0], :img_box.shape[1]]
    if OVERLAP_TILES or not np.any(img_box[mask]):
        img_box[mask] = box['tile'][:img_box.shape[0], :img_box.shape[1], :][mask]+
+       if len(img_box[mask]) > 0:
+           return True, mode_color(box['tile'])[0] # return the tile's mode color
+   return False, None
def create_tiled_image(boxes, res, render=False):
    print('Creating tiled image')
    img = np.zeros(shape=(res[0], res[1], 4), dtype=np.uint8)
+   n_tiles_placed = defaultdict(int)

    for box in tqdm(sorted(boxes, key=lambda x: x['min_dist'], reverse=OVERLAP_TILES)):
+       result, color = place_tile(img, box)
        if render:
            show_image(img, wait=False)
            sleep(0.025)
+       if result:
+           n_tiles_placed[color] += 1
+
+   for color, freq in sorted(n_tiles_placed.items(), key=lambda x: x[1], reverse=True):
+       print(f'{freq},{tuple(reversed(color))}')

    return img

This will print the colors and their frequency, sorted by the frequency. For example, the following image
image
will have the output:

281,(240, 240, 180)
129,(60, 60, 60)
102,(240, 240, 240)
86,(180, 120, 60)
39,(120, 60, 60)
35,(240, 60, 60)
32,(180, 180, 180)

If we plot the output we get
plot
which is consistent with the obtained image.

Keep in mind that this is based on the mode color (i.e., the most common color in the tile). Also, remember that a big tile counts just as much as a smaller one.

Let me know if this is what you wanted to achieve.

The plot above was actually done manually using Excel, but it can be easily automated using matplotlib:

import sys
import matplotlib.pyplot as plt

X = []
Y = []
colors = []

# read file and parse data
with open(sys.argv[1]) as f:
    for line in f:
        freq, color = line.split(',', 1)
        X.append(color.replace('\n', ''))
        Y.append(int(freq))
        color = color.replace('(', '').replace(')', '').split(',')
        colors.append([int(c) / 255 for c in color])

# plot data
plt.bar(X, Y, color=colors)
plt.ylabel('Number of tiles')
plt.xlabel('Color')
plt.xticks(rotation='65')
plt.title('Number of tiles per color')
plt.tight_layout()
plt.savefig('plot.png')

This script receives as input a file with the frequency and the colors, e.g.:

281,(240, 240, 180)
129,(60, 60, 60)
102,(240, 240, 240)
86,(180, 120, 60)
39,(120, 60, 60)
35,(240, 60, 60)
32,(180, 180, 180)

It saves the output to a file named plot.png:
plot

Glad to have helped!