2021-10-16 08:49:48 +02:00

230 lines
7.4 KiB
Python

import os
import json
import numpy as np
import random
from itertools import cycle
from skimage import transform
from sklearn.externals import joblib
from sklearn.metrics import log_loss
def binary_crossentropy(y_true, y_pred):
return log_loss(y_true.flatten(), y_pred.flatten())
def get_bands(band1, band2):
return np.dstack((
np.array(band1).reshape(75, 75),
np.array(band2).reshape(75, 75)
))
def get_angle(a):
if a == 'na':
return None
else:
return a
def get_label(l):
if l is None:
return l
else:
return int(l)
def unpack(samples):
images, angles, labels = zip(*[
(get_bands(s['band_1'], s['band_2']),
get_angle(s['inc_angle']),
get_label(s.get('is_iceberg')))
for s in samples
])
return images, angles, labels
def load_samples(datadir, filename):
with open(os.path.join(datadir, filename)) as f:
samples = json.load(f)
return samples
def train_dev_split(samples, split, shuffle=True):
split_idx = int(split*len(samples))
if shuffle:
random.seed(0)
random.shuffle(samples)
return (samples[0:split_idx], samples[split_idx:])
def write_preds(datadir, filename, samples, y_pred):
with open(os.path.join(datadir, 'predictions', filename), 'w') as f:
f.write('id,is_iceberg\n')
f.write('\n'.join([
'%s,%.6f' % (samples[i].get('id'), y_pred[i][0])
for i in range(len(samples))
]))
f.write('\n')
def model_fp(datadir, filename):
return os.path.join(datadir, 'params', filename)
def save_minmax(datadir, filename, minmax):
joblib.dump(minmax, os.path.join(datadir, 'params', filename))
def load_minmax(datadir, filename):
return joblib.load(os.path.join(datadir, 'params', filename))
def get_minmax(samples):
# Extract global mins/max' for normalization
images, angles, labels = unpack(samples)
b1_min = min([im[:, :, 0].min() for im in images])
b1_max = max([im[:, :, 0].max() for im in images])
b2_min = min([im[:, :, 1].min() for im in images])
b2_max = max([im[:, :, 1].max() for im in images])
a_min = min(angles, key=lambda x: x or 1e9)
a_max = max(angles, key=lambda x: x or -1e9)
print("Band 1 min/max: %.2f, %.2f" % (b1_min, b1_max))
print("Band 2 min/max: %.2f, %.2f" % (b2_min, b2_max))
print("Angles min/max: %.2f, %.2f" % (a_min, a_max))
return (b1_min, b1_max, b2_min, b2_max, a_min, a_max)
def base_cnn_generator(samples, minmax, batch_size, verbose=False):
img_dim = 75
window = 28
r = int(window/2)
padding = int(window/np.sqrt(2))
if verbose:
print("window is %d px" % window)
print("padding is %d px" % padding)
# Fuck the angles.
images, _, labels = unpack(samples)
b1_min, b1_max, b2_min, b2_max, _, _ = minmax
# Yield batches forever, cycling the samples with augmentation:
# Random rotation and crop, and random Bernoulli mirroring.
batch_images = []
batch_labels = []
for image, label in cycle(zip(images, labels)):
# Scale each band independently
img = np.dstack((
(image[:, :, 0] - b1_min) / (b1_max - b1_min),
(image[:, :, 1] - b2_min) / (b2_max - b2_min),
))
# Save the maximum of both bands within this image
b1_peak = img[:, :, 0].max()
b2_peak = img[:, :, 1].max()
# Rotate around random midpoint
row_mid = random.randint(padding, img_dim-padding-1)
col_mid = random.randint(padding, img_dim-padding-1)
if verbose:
print("\nSample %d" % (len(batch_labels)+1))
print("row/col mid: %d/%d" % (row_mid, col_mid))
print("Mid values: %.2f, %.2f" % (img[row_mid, col_mid, 0], img[row_mid, col_mid, 1]))
# NOTE: sklearn.transform.rotate behaves incorrectly when using kwargs 'center'
# Clip to mid with padding before rotating.
img = img[row_mid-padding:row_mid+padding, col_mid-padding:col_mid+padding, :]
rot = random.random() * 360 - 180
img = transform.rotate(img, rot, order=1)
if verbose:
print("Rotation: %.2f deg" % rot)
print("Mid values: %.2f, %.2f" % (img[padding, padding, 0], img[padding, padding, 1]))
img = img[padding-r:padding+r, padding-r:padding+r, :]
# Mirror
if random.randint(0, 1) and False:
img = np.fliplr(img)
if verbose:
print("Sample was mirrored")
# Label by checking that subimage contains maximum of either band
# If the subimage contains a max value that is within 95% of the image
# max, consider it to contain the feature.
sub_b1_peak = img[:, :, 0].max()
sub_b2_peak = img[:, :, 1].max()
l = int(sub_b1_peak/b1_peak > 0.95 and
sub_b2_peak/b2_peak > 0.95) * (label + 1)
if verbose:
print("Label is %d" % l)
categorical_label = tuple(int(i==l) for i in range(3))
batch_images.append(img)
batch_labels.append(categorical_label)
if len(batch_labels) == batch_size:
yield (
np.array(batch_images, dtype=np.float32),
np.array(batch_labels, dtype=np.float32)
)
batch_images = []
batch_labels = []
def augment_image(img, mirror, rots):
if mirror:
img = np.fliplr(img)
img = np.rot90(img, k=rots)
return img
def icenet_generator(samples, minmax, batch_size, crop_offset=3,
augment=True, verbose=False):
img_dim = 75
mid_x = mid_y = 75.0 / 2
crop_dim = img_dim - 2*crop_offset
window = 28
r = int(window/2)
images, _, labels = unpack(samples)
b1_min, b1_max, b2_min, b2_max, _, _ = minmax
# Yield batches forever
# A batch consists of an input-output tuple
# ([X_image_sec1, X_image_sec2, ... , X_image_sec9], Y_labels)
# where the input image is split over 9 overlapping sections, starting
# from upper left in row-major order
batch_image_sections = [[] for _ in range(9)]
batch_labels = []
for image, label in cycle(zip(images, labels)):
# Scale each band independently
img = np.dstack((
(image[:, :, 0] - b1_min) / (b1_max - b1_min),
(image[:, :, 1] - b2_min) / (b2_max - b2_min),
))
# Crop with random offset from midpoint
row_offset = crop_offset * (2*random.random() - 1)
col_offset = crop_offset * (2*random.random() - 1)
if not augment:
row_offset = col_offset = 0
row = int(mid_y + row_offset - crop_dim/2)
col = int(mid_x + col_offset - crop_dim/2)
img = img[row:row+crop_dim, col:col+crop_dim, :]
if augment:
img = augment_image(img, random.randint(0, 1), random.randint(0, 3))
# Append each section of the image
for i, ridx in enumerate([r, int(mid_y), crop_dim-r]):
for j, cidx in enumerate([r, int(mid_x), crop_dim-r]):
batch_image_sections[i*3+j].append(img[ridx-r:ridx+r, cidx-r:cidx+r, :])
batch_labels.append(label)
if len(batch_labels) == batch_size:
yield (
[np.array(sec_imgs, dtype=np.float32) for sec_imgs in batch_image_sections],
np.array(batch_labels, dtype=np.float32)
)
batch_image_sections = [[] for _ in range(9)]
batch_labels = []