Core espiownage parts

General logging

sysinfo[source]

sysinfo()

Prints out versions of PyTorch, CUDA, name of GPU and host machine (for logging)

sysinfo()
TORCH_VERSION=torch1.9.0; CUDA_VERSION=cu102
CUDA available = True, Device count = 1, Current device = 0
Device name = NVIDIA GeForce RTX 2070 with Max-Q Design
hostname: oryxpro

Files and filename manipulation

mkdir_if_needed[source]

mkdir_if_needed(path:str)

Parameters:

  • path : <class 'str'>

get_data[source]

get_data(dataset_name='cleaner', force_download=False, dest_root='~/.espiownage/data')

We 'anonymized' our URLs for double-blind review. But this screwed up fastai's downloader so we wrote one

Parameters:

  • dataset_name : <class 'str'>, optional

    'cleaner','preclean','spnet','cyclegan','fake'

  • force_download : <class 'bool'>, optional

    re-downloads even if you've already got one

  • dest_root : <class 'str'>, optional

    where the data gets saved

path = get_data('spnet'); path
Path('/home/drscotthawley/.espiownage/data/espiownage-spnet')

get_checkpoint[source]

get_checkpoint(model_type='segreg', force_download=False, dest_root='~/.espiownage/models')

For inference: Grab checkpoint of a pretrained espiownage model and return path to its location

Parameters:

  • model_type : <class 'str'>, optional

    'segreg', 'rings', 'bboxes' . trained on real data only

  • force_download : <class 'bool'>, optional

    re-downloads even if you've already got one

  • dest_root : <class 'str'>, optional

    where the model weights gets saved

path = get_checkpoint('segreg'); path
curl -L -o /home/drscotthawley/.espiownage/models/seg_reg_full_real_2.pth https://www.dropbox.com/s/4dnad92r37ji0ah/seg_reg_full_real_2.pth?dl=0
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   148    0   148    0     0    465      0 --:--:-- --:--:-- --:--:--   465
100   318  100   318    0     0    436      0 --:--:-- --:--:-- --:--:--   436
100  1076    0  1076    0     0    614      0 --:--:--  0:00:01 --:--:--  1415
  9  632M    9 61.6M    0     0   214k      0  0:50:16  0:04:54  0:45:22     0
Path('/home/drscotthawley/.espiownage/models/seg_reg_full_real_2.pth')

meta_to_img_path[source]

meta_to_img_path(meta_file:str, img_bank='images/')

Suggest the image file that corresponds with an annotation CSV file

Parameters:

  • meta_file : <class 'str'>

    filename of .csv file with annotations

  • img_bank : <class 'str'>, optional

    alternate location of image files, if not in same dir as meta file

meta_to_img_path('annotations/06241902_proc_00023.csv')
Path('images/06241902_proc_00023.png')

meta_to_mask_path[source]

meta_to_mask_path(meta_file:str, mask_dir='masks/')

provides name for segmentation mask file

Parameters:

  • meta_file : <class 'str'>

    filename of .csv file with annotations

  • mask_dir : <class 'str'>, optional

    output directory; assumed to exist

meta_to_mask_path('annotations/06241902_proc_00023.csv')
Path('masks/06241902_proc_00023_P.png')

meta_from_str[source]

meta_from_str(s)

e.g., 06241902_proc_00510_0_45_88_236_1.0 -> 06241902_proc_00510.csv

Parameters:

  • s : <class 'inspect._empty'>
assert meta_from_str('06241902_proc_00510_0_45_88_236_1.0.csv') == '06241902_proc_00510.csv'
assert meta_from_str('06241902_proc_00510_0_45_88_236.csv') == '06241902_proc_00510.csv'
assert meta_from_str('06241902_proc_00510.png') == '06241902_proc_00510.csv'

combine_file_and_tl_lists[source]

combine_file_and_tl_lists(file_list, top_loss_list)

for ellipse editor, but adding here: Go through tl list, and if elem e is not in file list, don't copy then go through file_list, and if e is already in, don't add

Parameters:

  • file_list : <class 'inspect._empty'>

  • top_loss_list : <class 'inspect._empty'>

file_list = ['annotations_shawley/06241902_proc_00000.csv', 'annotations_shawley/06240907_proc_01001.csv', 'annotations_shawley/06241902_proc_00004.csv', 'annotations_shawley/06240907_proc_01756.csv', 'annotations_shawley/06241902_proc_00009.csv', 'annotations_shawley/06241902_proc_00013.csv']
top_loss_list = ['06241902_proc_00510_0_45_88_236_1.0', '06240907_proc_01001_0_115_187_322_2.4', '06240907_proc_01197_0_105_185_338_3.33', '06241902_proc_00986_136_73_293_214_2.0', '06241902_proc_01468_0_58_81_211_3.75', '06240907_proc_01756_0_132_158_315_1.3', '06240907_proc_01101_0_101_172_326_4.0']
print("file_list =",file_list)
print("\ntop_lost_list =",top_loss_list)
out = combine_file_and_tl_lists(file_list, top_loss_list)
print("\nout = ",out)
file_list = ['annotations_shawley/06241902_proc_00000.csv', 'annotations_shawley/06240907_proc_01001.csv', 'annotations_shawley/06241902_proc_00004.csv', 'annotations_shawley/06240907_proc_01756.csv', 'annotations_shawley/06241902_proc_00009.csv', 'annotations_shawley/06241902_proc_00013.csv']

top_lost_list = ['06241902_proc_00510_0_45_88_236_1.0', '06240907_proc_01001_0_115_187_322_2.4', '06240907_proc_01197_0_105_185_338_3.33', '06241902_proc_00986_136_73_293_214_2.0', '06241902_proc_01468_0_58_81_211_3.75', '06240907_proc_01756_0_132_158_315_1.3', '06240907_proc_01101_0_101_172_326_4.0']

out =  ['annotations_shawley/06240907_proc_01001.csv', 'annotations_shawley/06240907_proc_01756.csv', 'annotations_shawley/06241902_proc_00000.csv', 'annotations_shawley/06241902_proc_00004.csv', 'annotations_shawley/06241902_proc_00009.csv', 'annotations_shawley/06241902_proc_00013.csv']

meta_to_df[source]

meta_to_df(meta_file)

Reads in an espiownage/SPNet CSV file of ellipse data and returns a Pandas DataFrame

Parameters:

  • meta_file : <class 'inspect._empty'>

    csv file of ellipse data for an image

Ellipses and math

fix_abangle[source]

fix_abangle(a:float, b:float, angle:float)

Makes sure semimajor axis > semiminor axis, and angles are consistent

Parameters:

  • a : <class 'float'>

    semimajor axis

  • b : <class 'float'>

    semiminor axis

  • angle : <class 'float'>

    orientation angle in degrees

fix_abangle(5,10,-20)
(10, 5, 70)

draw_ellipse[source]

draw_ellipse(img, center:tuple, axes:tuple, angle:float, color=0, thickness=2, filled=False, lineType=8, startAngle=0, endAngle=360, shift=0)

"Draws an ellipse into image.

Parameters:

  • img : <class 'inspect._empty'>

    a cv2 image, *not* a PIL image (similar for grayscale but not RGB)

  • center : <class 'tuple'>

    (cx, cy) tuple

  • axes : <class 'tuple'>

    (a,b) semimajor & minor axes

  • angle : <class 'float'>

    orientation angle in degrees

  • color : <class 'int'>, optional

    color to draw. tuple or int

  • thickness : <class 'int'>, optional

    thickness ofthe lines we draw

  • filled : <class 'bool'>, optional

    whether to draw the ellipse as filled or not

  • lineType : <class 'int'>, optional

    as opposed to LINE_AA, typically we DON'T want antialiasing for this app

  • startAngle : <class 'int'>, optional

  • endAngle : <class 'int'>, optional

    arc-angles. should stay at 0 & 360 for full ellipses.

  • shift : <class 'int'>, optional

    10, # shift is for sub-pixel resolution and AA figures. don't need it.

import matplotlib.pyplot as plt

height, width = 512,384
cx, cy, a, b, angle = 157, 213, 85, 67, 45
img = np.zeros((width, height), dtype=np.uint8)  # numpy w/h is "backwards" to images
img = draw_ellipse(img, (cx,cy), (a,b), angle, color=100, filled=False)
plt.imshow(img)
<matplotlib.image.AxesImage at 0x7f3c993a0970>

Let's check what "colors" are in that file, e.g. to make sure there's no anti-aliasing

print("'colors' =",set(np.array(img).flatten()))
'colors' = {0, 100}

And again, a filled version:

img = draw_ellipse(img, (cx,cy), (a,b), angle, color=100, filled=True)
print("'colors' =",set(np.array(img).flatten()))
plt.imshow(img)
'colors' = {0, 100}
<matplotlib.image.AxesImage at 0x7f3c993897c0>

ellipse_to_bbox[source]

ellipse_to_bbox(cx:float, cy:float, a:float, b:float, angle_deg:float, coco=False, width=512, height=384, clip=True, nozero=True)

converts ellipse to bounding box

Parameters:

  • cx : <class 'float'>

    x-coordinate of center of ellipse

  • cy : <class 'float'>

    y-coordinate of center of ellipse

  • a : <class 'float'>

    semimajor axis

  • b : <class 'float'>

    semiminor axis

  • angle_deg : <class 'float'>

    orientation angle in degrees

  • coco : <class 'bool'>, optional

    COCO style bbox has last 2 nums as width & height of bbox

  • width : <class 'int'>, optional

  • height : <class 'int'>, optional

    image dimensions for clipping

  • clip : <class 'bool'>, optional

    clip values at max values of image width & height

  • nozero : <class 'bool'>, optional

    Lots of downstream apps hate zero-dimension bounding box. This returns None

bb = ellipse_to_bbox(cx, cy, a, b, angle)
print("bbox = ",bb)
img = cv2.rectangle(img, bb[0:2], bb[2:4], color=50, thickness=2)
print("'colors' =",set(np.array(img).flatten()))
plt.imshow(img)
bbox =  (80, 136, 233, 289)
'colors' = {0, 50, 100}
<matplotlib.image.AxesImage at 0x7f3c992fd730>
ellipse_to_bbox(cx, cy, a, b, angle, coco=True)
[80.47, 136.47, 153.06, 153.06]
ellipse_to_bbox(0, 0, 0, 0, 0)  # check for (graceful handling of) errors
ellipse_to_bbox: Error: zero-dim bbox = (0, 0, 0, 0). Returning None.

ring_float_to_class_int[source]

ring_float_to_class_int(rings:float, step=0.1)

Ring value rounded to classifier value; rounded to nearest step size

Parameters:

  • rings : <class 'float'>

  • step : <class 'float'>, optional

ring_float_to_class_int(10.5, 0.1), ring_float_to_class_int(10.5, 0.2), ring_float_to_class_int(10.5, 1)
(105, 52, 10)

crop_to_bbox[source]

crop_to_bbox(img, bbox, coco=False, width=512, height=384, clip=True)

Crops an image to bbox, returns cropped image

Parameters:

  • img : <class 'inspect._empty'>

    an image (PIL preferred, but will convert from cv2 if neede)

  • bbox : <class 'inspect._empty'>

    [xmin, ymin, d3, d4] where d3,d4 are either xmax,ymax (default) or see coco (below)

  • coco : <class 'bool'>, optional

    COCO style input bbox has last 2 nums as width & height of bbox

  • width : <class 'int'>, optional

  • height : <class 'int'>, optional

    image dimensions for clipping

  • clip : <class 'bool'>, optional

    clip values at max values of image width & height

from matplotlib.pyplot import imshow

imshow(crop_to_bbox(img, bb))
<matplotlib.image.AxesImage at 0x7f3c9926b0a0>
bb_bad = crop_to_bbox(img, [-100,-100,-10,10])
crop_to_bbox: Error: zero-dim crop request, crop_bb = (0, 0, 0, 10). Returning None.

Hopefully we didn't crop in place and the original full-size image is still there?

plt.imshow(img)
<matplotlib.image.AxesImage at 0x7f3c991b8790>

is_in_box[source]

is_in_box(p, bb)

Is this point within that bounding box?

Parameters:

  • p : <class 'inspect._empty'>

    a point as a (x,y) coordinate pair

  • bb : <class 'inspect._empty'>

    a bounding box of the form [xmin,ymin,xmax,ymax]

assert is_in_box((100,100),(10,30,180,50)) == False
assert is_in_box((100,100),(10,30,180,150)) == True

"Regression accuracy" metric(s)

acc_reg[source]

acc_reg(inp, targ, bin_size=1)

Accuracy for regression: Are we within +/- bin_size?

Parameters:

  • inp : <class 'inspect._empty'>

  • targ : <class 'inspect._empty'>

  • bin_size : <class 'int'>, optional

Let's test this metric:

a = torch.rand(10)
b = a + torch.rand(10)
print(a)
print(b)
tol = 0.7
print( (a - b).abs() < tol)
print(acc_reg(a,b,bin_size=tol))
tensor([0.7457, 0.7263, 0.3642, 0.8471, 0.4808, 0.5804, 0.6998, 0.9799, 0.4964,
        0.2416])
tensor([0.9151, 1.2286, 0.4012, 1.5131, 0.6677, 1.0552, 1.1226, 1.0571, 1.1422,
        0.6349])
tensor([True, True, True, True, True, True, True, True, True, True])
TensorBase(1.)

We'll use a few predefined intervals:

acc_reg05[source]

acc_reg05(inp, targ)

Parameters:

  • inp : <class 'inspect._empty'>

  • targ : <class 'inspect._empty'>

acc_reg07[source]

acc_reg07(inp, targ)

Parameters:

  • inp : <class 'inspect._empty'>

  • targ : <class 'inspect._empty'>

acc_reg1[source]

acc_reg1(inp, targ)

Parameters:

  • inp : <class 'inspect._empty'>

  • targ : <class 'inspect._empty'>

acc_reg15[source]

acc_reg15(inp, targ)

Parameters:

  • inp : <class 'inspect._empty'>

  • targ : <class 'inspect._empty'>

acc_reg2[source]

acc_reg2(inp, targ)

Parameters:

  • inp : <class 'inspect._empty'>

  • targ : <class 'inspect._empty'>

Workflow utils

kfold_split[source]

kfold_split(data, k, nk=5)

Breaks one list into train & val as per k-fold cross-validation. For use with fastai IndexSplitter (throw out train_list) or IceVision's FixedSplitter

Parameters:

  • data : <class 'inspect._empty'>

    list of indices or data points to split into train & val

  • k : <class 'inspect._empty'>

    where we are in the iteration

  • nk : <class 'int'>, optional

    number of folds

data = list(range(10))
nk = 5
for k in range(nk):
    print(kfold_split(data, k))
([2, 3, 4, 5, 6, 7, 8, 9], [0, 1])
([0, 1, 4, 5, 6, 7, 8, 9], [2, 3])
([0, 1, 2, 3, 6, 7, 8, 9], [4, 5])
([0, 1, 2, 3, 4, 5, 8, 9], [6, 7])
([0, 1, 2, 3, 4, 5, 6, 7], [8, 9])