a b/bc-count/main.py
1
##############################################
2
#                                            #
3
# Main program (later this will be changed)  #
4
# for simplicities sake this will only call  # 
5
# function from algorithms/<>.py files       # 
6
#                                            #
7
# Author: Amine Neggazi                      #
8
# Email: neggazimedlamine@gmail/com          #
9
# Nick: nemo256                              #
10
#                                            #
11
# Please read bc-count/LICENSE               #
12
#                                            #
13
##############################################
14
15
import glob
16
import os
17
import cv2
18
import numpy as np
19
import tensorflow as tf
20
import matplotlib.pyplot as plt
21
from scipy import ndimage
22
from skimage.segmentation import watershed
23
from skimage.feature import peak_local_max
24
25
# custom imports
26
from config import *
27
import data
28
from model import do_unet, segnet, get_callbacks
29
30
31
def train(model_name='mse', epochs=50):
32
    '''
33
    This is the train function, so that we can train multiple models
34
    according to blood cell types and multiple input shapes aswell.
35
    :param model_name --> model weights that we want saved
36
    :param epochs --> how many epochs we want the model to be trained
37
38
    :return --> saves the model weights under <model_name>.h5 with
39
    its respective history file <model_name>_history.npy
40
    '''
41
42
    train_img_list = sorted(glob.glob(f'data/{cell_type}/train/image/*.jpg'))
43
    test_img_list = sorted(glob.glob(f'data/{cell_type}/test/image/*.jpg'))
44
    train_mask_list = sorted(glob.glob(f'data/{cell_type}/train/mask/*.jpg'))
45
    test_mask_list = sorted(glob.glob(f'data/{cell_type}/test/mask/*.jpg'))
46
47
    if cell_type == 'rbc':
48
        train_edge_list = sorted(glob.glob(f'data/{cell_type}/train/edge/*.jpg'))
49
        test_edge_list = sorted(glob.glob(f'data/{cell_type}/test/edge/*.jpg'))
50
    elif cell_type == 'wbc' or cell_type == 'plt':
51
        train_edge_list = None
52
        test_edge_list = None
53
    else:
54
        print('Invalid blood cell type!\n')
55
        return
56
57
    # loading train dataset and test datasets
58
    train_dataset = data.generator(
59
        train_img_list,
60
        train_mask_list,
61
        train_edge_list,
62
        type='train'
63
    )
64
    test_dataset = data.generator(
65
        test_img_list,
66
        test_mask_list,
67
        test_edge_list,
68
        type='test'
69
    )
70
71
    # initializing the do_unet model
72
    if model_type == 'do_unet':
73
        model = do_unet()
74
    else:
75
        model = segnet()
76
77
    # create models directory if it does not exist
78
    if not os.path.exists('models/'):
79
        os.makedirs('models/')
80
81
    # Check for existing weights
82
    if os.path.exists(f'models/{model_name}.h5'):
83
        model.load_weights(f'models/{model_name}.h5')
84
85
    # fitting the model
86
    history = model.fit(
87
        train_dataset.batch(8),
88
        validation_data=test_dataset.batch(8),
89
        epochs=epochs,
90
        steps_per_epoch=125,
91
        max_queue_size=16,
92
        use_multiprocessing=True,
93
        workers=8,
94
        verbose=1,
95
        callbacks=get_callbacks(model_name)
96
    )
97
98
    # save the history
99
    np.save(f'models/{model_name}_history.npy', history.history)
100
101
102
def normalize(img):
103
    '''
104
    Normalizes an image
105
    :param img --> an input image that we want normalized
106
107
    :return np.array --> an output image normalized (as a numpy array)
108
    '''
109
    return np.array((img - np.min(img)) / (np.max(img) - np.min(img)))
110
111
112
def get_sizes(img,
113
              padding=padding[1],
114
              input=input_shape[0],
115
              output=output_shape[0]):
116
    '''
117
    Get full image sizes (x, y) to rebuilt the full image output
118
    :param img --> an input image we want to get its dimensions
119
    :param padding --> the default padding used on the test dataset
120
    :param input --> the input shape of the image (param: img)
121
    :param output --> the output shape of the image (param: img)
122
123
    :return couple --> a couple which contains the image dimensions as in (x, y)
124
    '''
125
    offset = padding + (output / 2)
126
    return [(len(np.arange(offset, img[0].shape[0] - input / 2, output)), len(np.arange(offset, img[0].shape[1] - input / 2, output)))]
127
128
129
def reshape(img,
130
            size_x,
131
            size_y):
132
    '''
133
    Reshape the full image output using the original sizes (x, y)
134
    :param img --> an input image we want to reshape
135
    :param size_x --> the x axis (length) of the input image (param: img)
136
    :param size_y --> the y axis (length) of the input image (param: img)
137
138
    :return img (numpy array) --> the output image reshaped according to the provided dimensions (size_x, size_y)
139
    '''
140
    return img.reshape(size_x, size_y, output_shape[0], output_shape[0], 1)
141
142
143
def concat(imgs):
144
    '''
145
    Concatenate all the output image chips to rebuild the full image
146
    :param imgs --> the images that we want to concatenate
147
148
    :return full_image --> the concatenation of all the provided images (param: imgs)
149
    '''
150
    return cv2.vconcat([cv2.hconcat(im_list) for im_list in imgs[:,:,:,:]])
151
152
153
def denoise(img):
154
    '''
155
    Remove noise from an image
156
    :param img --> the input image that we want to denoise (remove the noise)
157
158
    :return image --> the denoised output image
159
    '''
160
    # read the image
161
    img = cv2.imread(img)
162
    # return the denoised image
163
    # if cell_type == 'plt':
164
    #     return cv2.fastNlMeansDenoising(img, 19, 19, 7, 21)
165
    # return cv2.fastNlMeansDenoising(img, 23, 23, 7, 21)
166
    return img
167
168
169
def predict(imgName='Im037_0'):
170
    '''
171
    Predict (segment) blood cell images using the trained model (do_unet)
172
    :param img --> the image we want to predict (from the test/ directory)
173
174
    :return --> saves the predicted (segmented blood cell image) under the folder output/
175
    '''
176
    # Check for existing predictions
177
    if not os.path.exists(f'{output_directory}/{imgName}'):
178
        os.makedirs(f'{output_directory}/{imgName}', exist_ok=True)
179
    else:
180
        print('Prediction already exists!')
181
        return
182
183
    test_img = sorted(glob.glob(f'data/ALL-IDB1-FULL/{imgName}.jpg'))
184
185
    # initializing the do_unet model
186
    if model_type == 'do_unet':
187
        model = do_unet()
188
    else:
189
        model = segnet()
190
191
    # Check for existing weights
192
    if os.path.exists(f'models/{model_name}.h5'):
193
        model.load_weights(f'models/{model_name}.h5')
194
195
    # load test data
196
    img = data.load_image(test_img, padding=padding[0])
197
198
    img_chips = data.slice_image(
199
        img,
200
        padding=padding[1],
201
        input_size=input_shape[0],
202
        output_size=output_shape[0],
203
    )
204
205
    # segment all image chips
206
    output = model.predict(img_chips)
207
208
    if cell_type == 'rbc':
209
        new_mask_chips = np.array(output[0])
210
        new_edge_chips = np.array(output[1])
211
    elif cell_type == 'wbc' or cell_type == 'plt':
212
        new_mask_chips = np.array(output)
213
214
    # get image dimensions
215
    dimensions = [get_sizes(img)[0][0], get_sizes(img)[0][1]]
216
217
    # reshape chips arrays to be concatenated
218
    new_mask_chips = reshape(new_mask_chips, dimensions[0], dimensions[1])
219
    if cell_type == 'rbc':
220
        new_edge_chips = reshape(new_edge_chips, dimensions[0], dimensions[1])
221
222
    # get rid of none necessary dimension
223
    new_mask_chips = np.squeeze(new_mask_chips)
224
    if cell_type == 'rbc':
225
        new_edge_chips = np.squeeze(new_edge_chips)
226
227
    # concatenate chips into a single image (mask and edge)
228
    new_mask = concat(new_mask_chips)
229
    if cell_type == 'rbc':
230
        new_edge = concat(new_edge_chips)
231
    
232
    # save predicted mask and edge
233
    plt.imsave(f'{output_directory}/{imgName}/mask.png', new_mask)
234
    if cell_type == 'rbc':
235
        plt.imsave(f'{output_directory}/{imgName}/edge.png', new_edge)
236
        plt.imsave(f'{output_directory}/{imgName}/edge_mask.png', new_mask - new_edge)
237
238
239
def denoise_full_image(imgName='Im037_0'):
240
    # denoise all the output images
241
    new_mask  = denoise(f'{output_directory}/{imgName}/mask.png')
242
    if cell_type == 'rbc':
243
        new_edge  = denoise(f'{output_directory}/{imgName}/edge.png')
244
        edge_mask = denoise(f'{output_directory}/{imgName}/edge_mask.png')
245
246
    # save predicted mask and edge after denoising
247
    plt.imsave(f'{output_directory}/{imgName}/denoise.png', new_mask)
248
    if cell_type == 'rbc':
249
        plt.imsave(f'{output_directory}/{imgName}/edge.png', new_edge)
250
        plt.imsave(f'{output_directory}/{imgName}/edge_mask.png', edge_mask)
251
252
253
def evaluate(model_name='mse'):
254
    '''
255
    Evaluate an already trained model
256
    :param model_name --> the model weights that we want to evaluate
257
258
    :return --> output the evaluated model weights directly to the screen.
259
    '''
260
    test_img_list = sorted(glob.glob(f'data/{cell_type}/test/image/*.jpg'))
261
    test_mask_list = sorted(glob.glob(f'data/{cell_type}/test/mask/*.jpg'))
262
    if cell_type == 'rbc':
263
        test_edge_list = sorted(glob.glob(f'data/{cell_type}/test/edge/*.jpg'))
264
    elif cell_type == 'wbc' or cell_type == 'plt':
265
        test_edge_list = None
266
    else:
267
        print('Invalid blood cell type!\n')
268
        return
269
270
    # initializing the do_unet model
271
    if model_type == 'do_unet':
272
        model = do_unet()
273
    else:
274
        model = segnet()
275
276
    # load weights
277
    if os.path.exists(f'models/{model_name}.h5'):
278
        model.load_weights(f'models/{model_name}.h5')
279
    else:
280
        train(model_name)
281
282
    # load test data
283
    if cell_type == 'rbc':
284
        img, mask, edge = data.load_data(test_img_list, test_mask_list, test_edge_list, padding=padding[0])
285
286
        img_chips, mask_chips, edge_chips = data.slice(
287
            img,
288
            mask,
289
            edge,
290
            padding=padding[1],
291
            input_size=input_shape[0],
292
            output_size=output_shape[0]
293
        )
294
    elif cell_type == 'wbc' or cell_type == 'plt':
295
        img, mask = data.load_data(test_img_list, test_mask_list, padding=padding[0])
296
297
        img_chips, mask_chips = data.slice(
298
            img,
299
            mask,
300
            padding=padding[1],
301
            input_size=input_shape[0],
302
            output_size=output_shape[0]
303
        )
304
305
    # print the evaluated accuracies
306
    if cell_type == 'rbc':
307
        print(model.evaluate(img_chips, (mask_chips, edge_chips)))
308
    else:
309
        print(model.evaluate(img_chips, (mask_chips)))
310
311
312
def threshold(img='edge.png', imgName='Im037_0'):
313
    '''
314
    This is the threshold function, which applied an otsu threshold
315
    to the input image (param: img)
316
    :param img --> the image we want to threshold
317
318
    :return --> saves the output thresholded image under the folder output/<cell_type>/threshold_<img>.png
319
    '''
320
    if not os.path.exists(f'{output_directory}/{imgName}/{img}'):
321
        print('Image does not exist!')
322
        return
323
324
    # substract if img is edge_mask
325
    if img == 'edge_mask.png':
326
        mask = cv2.imread(f'{output_directory}/{imgName}/threshold_mask.png')
327
        edge = cv2.imread(f'{output_directory}/{imgName}/threshold_edge.png')
328
329
        # substract mask - edge
330
        image = mask - edge
331
    else:
332
        # getting the input image
333
        image = cv2.imread(f'{output_directory}/{imgName}/{img}')
334
335
        # convert to grayscale and apply otsu's thresholding
336
        image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
337
        if cell_type == 'plt':
338
            threshold, image = cv2.threshold(image, 67, 255, cv2.THRESH_BINARY)
339
        elif cell_type == 'wbc':
340
            threshold, image = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY)
341
        else:
342
            threshold, image = cv2.threshold(image, 0, 255, cv2.THRESH_OTSU)
343
344
    # save the resulting thresholded image
345
    plt.imsave(f'{output_directory}/{imgName}/threshold_{img}', image, cmap='gray')
346
    
347
348
def hough_transform(img='edge.png', imgName='Im037_0'):
349
    '''
350
    This is the Circle Hough Transform function (CHT), which counts the
351
    circles from an input image.
352
    :param img --> the input image that we want to count circles from.
353
354
    :return --> saves the output image under the folder output/<cell_type>/hough_transform.png
355
    '''
356
    if not os.path.exists(f'{output_directory}/{imgName}/{img}'):
357
        print('Image does not exist!')
358
        return
359
360
    # getting the input image
361
    image = cv2.imread(f'{output_directory}/{imgName}/{img}')
362
    # convert to grayscale
363
    img = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
364
365
    if cell_type == 'wbc':
366
        # apply surface filter
367
        img, ret_count = surfaceFilter(img, min_size=2000)
368
369
        img = ((img > 0) * 255.).astype(np.uint8)
370
371
    # apply hough circles
372
    if cell_type == 'rbc':
373
        if model_type == 'segnet':
374
            circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1, minDist=33, maxRadius=55, minRadius=28, param1=30, param2=20)
375
        else:
376
            circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1, minDist=33, maxRadius=56, minRadius=29, param1=28, param2=20)
377
    elif cell_type == 'wbc':
378
        if model_type == 'do_unet':
379
            circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1, minDist=51, maxRadius=120, minRadius=48, param1=70, param2=20)
380
        else:
381
            circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1, minDist=70, maxRadius=120, minRadius=33, param1=62, param2=12)
382
    elif cell_type == 'plt':
383
        circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1.3, minDist=16, maxRadius=25, minRadius=1, param1=13, param2=11)
384
    output = img.copy()
385
386
    # ensure at least some circles were found
387
    if circles is not None:
388
        # convert the (x, y) coordinates and radius of the circles to integers
389
        circles = np.round(circles[0, :]).astype("int")
390
        # loop over the (x, y) coordinates and radius of the circles
391
        for (x, y, r) in circles:
392
            # draw the circle in the output image, then draw a rectangle
393
            # corresponding to the center of the circle
394
            cv2.circle(output, (x, y), r, (0, 0, 255), 2)
395
            cv2.rectangle(output, (x - 5, y - 5), (x + 5, y + 5), (0, 0, 255), -1)
396
        # save the output image
397
        plt.imsave(f'{output_directory}/{imgName}/hough_transform.png',
398
                   np.hstack([img, output]))
399
        # show the hough_transform results
400
        print(f'Hough Transform: {len(circles)}')
401
        if len(circles) == None:
402
            return 0
403
        else:
404
            return len(circles)
405
    else:
406
        return 0
407
408
409
def component_labeling(img='edge.png', imgName='Im037_0'):
410
    '''
411
    This is the Connected Component Labeling (CCL), which labels all the connected objects from an input image
412
    :param img --> the input image that we want to apply CCL to.
413
414
    :return --> saves the output image under the folder output/<cell_type>/component_labeling.png
415
    '''
416
    if not os.path.exists(f'{output_directory}/{imgName}/{img}'):
417
        print('Image does not exist!')
418
        return
419
420
    # getting the input image
421
    image = cv2.imread(f'{output_directory}/{imgName}/{img}')
422
    # convert to grayscale
423
    img = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
424
    # # converting those pixels with values 1-127 to 0 and others to 1
425
    # img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)[1]
426
    # applying surfaceFilter
427
    if cell_type == 'wbc':
428
        result_image, ret_count = surfaceFilter(img, min_size=1400)
429
    elif cell_type == 'rbc':
430
        result_image, ret_count = surfaceFilter(img, min_size=200)
431
    else:
432
        result_image, ret_count = surfaceFilter(img)
433
434
    # saving image after Component Labeling
435
    plt.imsave(f'{output_directory}/{imgName}/component_labeling.png',
436
               np.hstack([img, result_image]))
437
438
    # show number of labels detected
439
    print(f'Connected Component Labeling: {ret_count - 1}')
440
    return ret_count - 1
441
442
443
def distance_transform(img='threshold_edge_mask.png', imgName='Im037_0'):
444
    '''
445
    This is the Euclidean Distance Transform function (EDT), which applied the distance transform algorithm to an input image>
446
    :param img --> the input image that we want to apply EDT to.
447
448
    :return --> saves the output image under the folder output/<cell_type>/distance_transform.png
449
    '''
450
    if not os.path.exists(f'{output_directory}/{imgName}/{img}'):
451
        print('Image does not exist!')
452
        return
453
454
    # getting the input image
455
    image = cv2.imread(f'{output_directory}/{imgName}/{img}')
456
    # convert to numpy array
457
    img = np.asarray(image)
458
    # convert to grayscale
459
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
460
461
    img = ndimage.distance_transform_edt(img)
462
463
    # saving image after Component Labeling
464
    plt.imsave(f'{output_directory}/{imgName}/distance_transform.png', img, cmap='gray')
465
466
467
def surfaceFilter(image, min_size = None, max_size = None):
468
    img = image.copy()
469
    ret, labels = cv2.connectedComponents(img)
470
    
471
    label_codes = np.unique(labels)
472
    result_image = labels
473
    
474
    if 9999 in result_image:
475
        print("error the image contains the null number 9999")
476
    
477
    i = 0
478
    background_index = 0
479
    max = 0
480
    for label in label_codes:
481
        count = (labels == label).sum()
482
483
        #find the background index
484
        if count > max:
485
            max = count
486
            background_index = i
487
488
        if min_size is not None and (count < min_size):
489
            result_image[labels == label] = 9999
490
            ret = ret - 1
491
492
        if max_size is not None and (count > max_size):
493
            result_image[labels == label] = 9999
494
            ret = ret - 1
495
        i = i + 1
496
    result_image[result_image == 9999] = label_codes[background_index]
497
    return result_image, ret
498
499
500
def count(img='threshold_mask.png', imgName='Im037_0'):
501
    if not os.path.exists(f'{output_directory}/{imgName}/{img}'):
502
        print('Image does not exist!')
503
        return
504
505
    # getting the input image
506
    image = cv2.imread(f'{output_directory}/{imgName}/{img}')
507
    # convert to numpy array
508
    img = np.asarray(image)
509
    # convert to grayscale
510
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
511
512
    if cell_type == 'rbc':
513
        min_distance = 40
514
        threshold_abs = None
515
        exclude_border = True
516
    elif cell_type == 'wbc':
517
        min_distance = 51
518
        threshold_abs = 24
519
        exclude_border = False
520
    elif cell_type == 'plt':
521
        min_distance = 20
522
        img = ndimage.binary_dilation(img)
523
        threshold_abs = 4
524
        exclude_border = False
525
526
    edt = ndimage.distance_transform_edt(img)
527
528
    coords = peak_local_max(edt, 
529
                            indices=True,
530
                            num_peaks=2000,
531
                            min_distance=min_distance, 
532
                            threshold_abs=threshold_abs,
533
                            exclude_border=exclude_border,
534
                            labels=img)
535
536
    # print(coords[:, 1])
537
    canvas = np.ones(img.shape + (3,), dtype=np.uint8) * 255
538
    i = 255
539
    for c in coords:
540
        o_c = (int(c[1]), int(c[0]))
541
        cv2.circle(canvas, o_c, 20, (i, 0, 0), -1)
542
        i = i - 1
543
544
    # saving image after counting
545
    plt.imsave(f'{output_directory}/{imgName}/output.png', canvas, cmap='gray')
546
    print(f'Euclidean Distance Transform: {len(coords)}')
547
    return len(coords)
548
549
550
def accuracy(real, predicted):
551
    acc = (1 - (np.absolute(int(predicted) - int(real)) / int(real))) * 100
552
    if int(real) == 0.:
553
        if int(predicted) == 0.:
554
            return 100.
555
        else:
556
            return 0.
557
558
    if acc <= 100. and acc > 0.:
559
        return acc
560
    elif acc < 0.:
561
        return np.absolute(acc / 100.)
562
    else:
563
        return 0.
564
565
566
def predict_all_idb():
567
    image_list = sorted(glob.glob('data/ALL-IDB1/*'))
568
    if not os.path.exists(output_directory):
569
        os.makedirs(output_directory, exist_ok=True)
570
571
    real_count = []
572
    with open(f'data/{cell_type}_count.txt', 'r+') as rc:
573
        file = rc.read().splitlines()
574
        for line in file:
575
            real_count += [line.split(' ')[-1]]
576
577
    i = 0
578
    cht_accuracy = []
579
    ccl_accuracy = []
580
    edt_accuracy = []
581
    with open(f'{output_directory}/{cell_type}_results.txt', 'a+') as r:
582
        r.write('Image Real_Count CHT CCL EDT CHT_Accuracy CCL_Accuracy EDT_Accuracy\n')
583
        for image in image_list:
584
            img = image.split('/')[-1].split('.')[0]
585
            print(f'--------------------------------------------------')
586
            predict(img)
587
            # denoise_full_image(img)
588
            threshold('mask.png', img)
589
            print(f'Image <-- {img} -->')
590
            print(f'Real Count: {real_count[i]}')
591
            if cell_type == 'rbc':
592
                threshold('edge.png', img)
593
                threshold('edge_mask.png', img)
594
                distance_transform('threshold_edge_mask.png', img)
595
                cht_count = hough_transform('edge.png', img)
596
            else:
597
                distance_transform('threshold_mask.png', img)
598
                cht_count = hough_transform('threshold_mask.png', img)
599
600
            edt_count = count('threshold_mask.png', img)
601
            ccl_count = component_labeling('threshold_mask.png', img)
602
            cht_accuracy += [accuracy(real_count[i], cht_count)]
603
            ccl_accuracy += [accuracy(real_count[i], ccl_count)]
604
            edt_accuracy += [accuracy(real_count[i], edt_count)]
605
            # accuracy = np.mean([cht_accuracy, ccl_accuracy])
606
            r.write(f'{img} {real_count[i]} {cht_count} {ccl_count} {edt_count} {np.round(cht_accuracy[i], 2)} {np.round(ccl_accuracy[i], 2)} {np.round(edt_accuracy[i], 2)}\n')
607
            i = i + 1
608
609
        r.write(f'Total -1 -1 -1 -1 {np.round(np.mean(cht_accuracy), 2)} {np.round(np.mean(ccl_accuracy), 2)} {np.round(np.mean(edt_accuracy), 2)}\n')
610
        if cell_type == 'rbc':
611
            print(f'Accuracy: {np.round(np.mean(cht_accuracy), 2)}%')
612
        elif cell_type == 'wbc':
613
            print(f'Accuracy: {np.round(np.mean(edt_accuracy), 2)}%')
614
        else:
615
            print(f'Accuracy: {np.round(np.mean(ccl_accuracy), 2)}%')
616
617
618
if __name__ == '__main__':
619
    '''
620
    The main function, which handles all the function call
621
    (later on, this will dynamically call functions according user input)
622
    '''
623
    # train('plt_segnet', epochs=12)
624
    # evaluate(model_name='plt_segnet')
625
    image = 'Im037_0'
626
    predict(imgName=image)
627
    # denoise_full_image(imgName=image)
628
    threshold('mask.png', image)
629
630
    if cell_type == 'rbc':
631
        threshold('edge.png', image)
632
        threshold('edge_mask.png', image)
633
        distance_transform('threshold_edge_mask.png', image)
634
        hough_transform('edge.png', image)
635
    else:
636
        distance_transform('threshold_mask.png', image)
637
        hough_transform('threshold_mask.png', image)
638
639
    count('threshold_mask.png', image)
640
    component_labeling('threshold_mask.png', image)
641
642
    # predict_all_idb()
643