Commit e72a6b54 authored by Marius Laska's avatar Marius Laska

added first version of rotating BBOX

parent c9b662ef
......@@ -276,7 +276,7 @@ def main(dataset="UJI"):
if __name__ == "__main__":
res_dict = {"Model": [], "MSE": [], "fold": []}
num_add_aug_ratio = []
dataset = "UJI"
dataset = "lohan"
f_idx = None
floor = None
box_df_concat = None
......
import math
from il_pipeline.pipeline import Pipeline
from il_pipeline.utility.storable import Storable
from pkg_resources import resource_filename
from analysis.visualize_learning_progress import convert_from_2dim_overlapping_rotated_grid
from base.floor_plan_plot import FloorPlanPlotRec
import numpy as np
from shapely.geometry import Polygon
def main():
p: Pipeline = Storable.load("/home/laskama/PycharmProjects/bboxPrediction/evaluation/lohan/rot/output/BBOX_1l_20_aug_1")
convert_from_2dim_overlapping_rotated_grid(p, grid_size=40,
quantile=False,
store=True,
average_samples=True)
y_pred_box = np.concatenate(p.summary.y_pred, axis=0)
img = resource_filename('data', 'lohan/CrowdsourcedDS1floor.png')
fp_dims = (200, 80)
fp = FloorPlanPlotRec(fp_dims, 2, floorplan_bg_img=img)
for idx in range(100): # len(y_true)):
poly = get_rotated_polygon(*y_pred_box[idx, :])
fp.draw_polygons([poly])
fp.draw_rectangles_new(anchors=y_pred_box[idx, :], color='black')
fp.show_plot()
def get_rotated_polygon(cx, cy, w, h, omega):
center = np.array([cx, cy])
ll = np.array([cx - w/2.0, cy - h/2.0]) - center
lr = np.array([cx + w/2.0, cy - h/2.0]) - center
ul = np.array([cx - w/2.0, cy + h/2.0]) - center
ur = np.array([cx + w/2.0, cy + h/2.0]) - center
exterior = [ll, lr, ur, ul]
exterior_r = []
for p in exterior:
px = p[0] * np.cos(omega * math.pi) - p[1] * np.sin(omega * math.pi)
py = p[1] * np.cos(omega * math.pi) + p[0] * np.sin(omega * math.pi)
exterior_r.append(np.array([px, py]) + center)
return Polygon(np.array(exterior_r))
if __name__ == '__main__':
main()
\ No newline at end of file
......@@ -361,6 +361,80 @@ def convert_from_2dim_overlapping_grid(pipe: Pipeline, grid_size=20, padding_rat
return res_folds
def convert_from_2dim_overlapping_rotated_grid(pipe: Pipeline, grid_size=20, padding_ratio=0.1, quantile=False, store=True, average_samples=True):
n_cols = math.ceil(pipe.data_provider.floorplan_width / grid_size)
res_folds = []
height = pipe.data_provider.floorplan_height
width = pipe.data_provider.floorplan_width
# Determine #row,col for 1st layer
num_row_l1 = np.ceil(height / grid_size) + 1
num_col_l1 = np.ceil(width / grid_size) + 1
num_l1 = int(num_row_l1 * num_col_l1)
num = num_l1
origins = []
# Determine centers & origins (lower-left)
for idx in range(num):
# 1st layer
r_idx = int(idx / num_col_l1)
c_idx = idx % num_col_l1
x = c_idx * grid_size
y = r_idx * grid_size
origins.append(np.array([x, y]))
# Determine closest origin for labels (the corresponding cell is
# responsible for encoding
origins = np.array(origins)
for fold_idx in range(pipe.data_provider.num_splits):
pred_vals = pipe.summary.y_pred[fold_idx]
if type(pred_vals) is not list:
# need to average
pred_val_samples = [pred_vals]
else:
pred_val_samples = pred_vals
pred_fold = np.zeros((len(pred_val_samples[0]), 5, len(pred_val_samples)))
for s_idx, pred_vals in enumerate(pred_val_samples):
sub = pred_vals[:, 5::6]
chosen = np.argmax(sub, axis=1)
for idx in range(len(pred_vals)):
local_box = pred_vals[idx,
chosen[idx] * 6:(chosen[idx] + 1) * 6 - 1]
local_box[:2] = origins[chosen[idx], :] + local_box[:2] * (grid_size / 2.0 + grid_size * padding_ratio)
# 3rd and 4th dimension contain width and height
# convert from [-1,1] to [0,1]
local_box[2:4] = (local_box[2:4] + 1) / 2.0
local_box[2:4] *= grid_size
pred_fold[idx, :, s_idx] = local_box
# average pred fold
if average_samples:
pred_fold = np.mean(pred_fold, axis=2)
if store:
pipe.summary.y_pred[fold_idx] = pred_fold
else:
res_folds.append(pred_fold)
if len(res_folds) > 0:
return res_folds
def convert_from_2dim_grid(pipe: Pipeline, grid_size=20, quantile=False, store=True, average_samples=True):
n_cols = math.ceil(pipe.data_provider.floorplan_width / grid_size)
res_folds = []
......
......@@ -15,7 +15,7 @@ from base.bbox_model_definition import bbox_model_for_generator
from base.hier_model_definition import hier_model_for_generator
from base.custom_loss import bbox_loss, define_bbox_loss, define_circle_loss, \
define_quantile_loss, \
define_single_quantile_loss, define_yolo_loss, define_grid_quantile_loss, define_yolo_loss_tanh, define_yolo_loss_tanh_no_size
define_single_quantile_loss, define_yolo_loss, define_grid_quantile_loss, define_yolo_loss_tanh, define_yolo_loss_tanh_no_size, define_rotated_box_loss
class BboxModel(DnnModel):
......@@ -75,6 +75,13 @@ class BboxModel(DnnModel):
params.update({'last_activation': tanh,
'losses': loss_func})
elif self.type == "GRID_ROT-BBOX":
if 'loss' in params:
loss_func = define_rotated_box_loss(params['loss'])
params.update({'last_activation': tanh,
'losses': loss_func})
elif self.type == "CIRCLE":
if 'loss' in params:
loss_func = define_circle_loss(params['loss'])
......@@ -171,6 +178,11 @@ class BboxModel(DnnModel):
y_cols = 5 * (n_row * n_col + (n_row+1)*(n_col) + (n_row - 1) * n_col)
if self.type == "GRID_ROT-BBOX":
y_cols = 6 * (math.ceil(
dp.floorplan_height / dp.grid_size) + 1) * (math.ceil(
dp.floorplan_width / dp.grid_size) + 1)
# obtain the model definition
if "BBOX" in self.type or "CIRCLE" in self.type or "QUANTILE" in self.type or "S_Q" in self.type:
classifier_template = bbox_model_for_generator(metrics=[])
......
......@@ -970,6 +970,136 @@ def define_yolo_loss_tanh_no_size(params):
return yolo_loss
def define_rotated_box_loss(params):
import math as m
tf_PI = tf.constant(m.pi)
def yolo_loss(y_true, y_pred):
loss = tf.zeros_like(y_true[:, 0])
# grid cell classification loss
grid_cell_pred = y_pred[:, 5::6]
# scale from [-1,1] to [0,1]
grid_cell_pred = tf.multiply(tf.add(grid_cell_pred, 1.0), 0.5)
# grid cell true
grid_cell_true = tf.one_hot(tf.cast(y_true[:, 2], dtype=tf.int32), depth=tf.shape(grid_cell_pred)[1])
class_loss = tf.reduce_sum(tf.squared_difference(grid_cell_pred, grid_cell_true), axis=1)
if "scale" in params["grid"]:
factor = params["grid"]["scale"]
class_loss = tf.multiply(class_loss, tf.constant(factor))
loss += class_loss
# pred subset
g_idx = y_true[:, 2]
# convert to 3-dim tensor
y_pred_sa = tf.reshape(y_pred, (tf.shape(y_true)[0], tf.shape(grid_cell_pred)[1], 6))
# gather along 2nd dimension (Grids)
grid_sub = tf.gather(y_pred_sa, tf.cast(g_idx,tf.int32), axis=1)#[0,0,:]
# Read dimensions
# (see: https://stackoverflow.com/questions/57387169/
# getting-the-diagonal-elements-of-only-part-of-a-tensor)
s = tf.shape(grid_sub)
# Make indices for gathering
ii, jj = tf.meshgrid(tf.range(s[0]), tf.range(s[2]), indexing='ij')
idx = tf.stack([ii, ii, jj], axis=-1)
# Gather result
y_pred_s = tf.gather_nd(grid_sub, idx)
#
# SCALE tanh box size output to [0,1]
#
zeros = tf.zeros([tf.shape(y_pred_s)[0], 2])
ones = tf.ones([tf.shape(y_pred_s)[0], 2])
scale_add = tf.concat([zeros, ones, tf.zeros([tf.shape(y_pred_s)[0], 2])], axis=1)
y_pred_s = tf.add(y_pred_s, scale_add)
# rotate (c_x, c_y) and (t_x, t_y) by omega * PI around center of grid cell
omega = y_pred_s[:, 4]
rot_cx = tf.multiply(y_pred_s[:, 0], tf.cos(tf_PI * omega)) - tf.multiply(y_pred_s[:, 1], tf.sin(tf_PI * omega))
rot_cy = tf.multiply(y_pred_s[:, 1], tf.cos(tf_PI * omega)) + tf.multiply(y_pred_s[:, 0], tf.sin(tf_PI * omega))
rot_tx = tf.multiply(y_true[:, 0], tf.cos(tf_PI * omega)) - tf.multiply(y_true[:, 1], tf.sin(tf_PI * omega))
rot_ty = tf.multiply(y_true[:, 1], tf.cos(tf_PI * omega)) + tf.multiply(y_true[:, 0], tf.sin(tf_PI * omega))
y_true = tf.concat([tf.reshape(rot_tx, [-1, 1]), tf.reshape(rot_ty,[-1, 1]), tf.reshape(y_true[:, 2],[-1, 1])], axis=1)
y_pred_s = tf.concat([tf.reshape(rot_cx, [-1, 1]), tf.reshape(rot_cy,[-1, 1]), y_pred_s[:, 2:]], axis=1)
# box loss
box_loss = tf.zeros_like(y_true[:, 0])
box_loss_scale = tf.reduce_sum(tf.multiply(grid_cell_pred, grid_cell_true))
# center loss
c_loss = tf.reduce_sum(tf.square(tf.subtract(y_true[:, :2], y_pred_s[:, :2])),
axis=1)
box_loss += c_loss
# if "size" in params:
# size_loss = tf.reduce_sum(tf.square(y_pred_s[:, 2:]), axis=1)
#
# if "threshold" in params["size"]:
# # only count size loss if greater than 50.0
# threshold = params["size"]["threshold"]
# size_loss = tf.maximum(tf.zeros_like(size_loss),
# tf.subtract(size_loss, tf.multiply(
# tf.ones_like(size_loss), threshold)))
#
# if "scale" in params["size"]:
# factor = params["size"]["scale"]
# size_loss = tf.multiply(size_loss, tf.constant(factor))
#
# box_loss += size_loss
if "outside" in params:
delta = 20.0
if "delta" in params['outside']:
delta = params['outside']['delta']
outside_x = tf.subtract(tf.subtract(
tf.abs(tf.subtract(y_true[:, 0], y_pred_s[:, 0])),
tf.divide(y_pred_s[:, 2], tf.constant(delta))),
tf.constant(0.0))
outside_y = tf.subtract(tf.subtract(
tf.abs(tf.subtract(y_true[:, 1], y_pred_s[:, 1])),
tf.divide(y_pred_s[:, 3], tf.constant(delta))),
tf.constant(0.0))
outside_x_loss = outside_x # tf.maximum(tf.zeros_like(outside_x), outside_x)
outside_y_loss = outside_y # tf.maximum(tf.zeros_like(outside_y), outside_y)
outside_loss = tf.square(outside_x_loss) + tf.square(outside_y_loss)
#outside_loss = outside_x_loss + outside_y_loss
if "scale" in params["outside"]:
factor = params["outside"]["scale"]
outside_loss = tf.multiply(outside_loss, tf.constant(factor))
box_loss += outside_loss
loss += box_loss * box_loss_scale
return loss
return yolo_loss
def define_yolo_loss(params):
def yolo_loss(y_true, y_pred):
......@@ -1204,7 +1334,7 @@ def test_yolo_loss():
y_true = np.array([[0.6, 0.3, 0], [0.1, 0.05, 1]])
y_pred = np.array([[0.7, 0.3, 0.5, 0.2, 0.6, 0.4, 0.2, 0.9, 0.1, -0.4], [0.3, 0.2, 0.4, 0.1, -0.2, 0.2, 0.2, 0.6, 0.1, -0.8]])
y_pred = np.array([[0.7, 0.3, 0.5, 0.2, 0, 0.6, 0.4, 0.2, 0.9, 0.1,0, -0.4], [0.3, 0.2, 0.4, 0.1, 0, -0.2, 0.2, 0.2, 0.6, 0.1,0, -0.8]])
#c_sub = y_pred[:, [0::, 1]*5]
......@@ -1214,7 +1344,7 @@ def test_yolo_loss():
y_true = tf.constant(y_true, dtype=tf.float32)
y_pred = tf.constant(y_pred, dtype=tf.float32)
loss_func = define_yolo_loss_tanh(params=dict(grid={"scale": 4.0}, size={"scale": 0.0055}, outside={"scale": 0.1}))
loss_func = define_rotated_box_loss(params=dict(grid={"scale": 4.0}, size={"scale": 0.0055}, outside={"scale": 0.1}))
with tf.Session() as sess:
# Run the initializer on `w`.
......
data:
# The data provider which should be used
provider: LohanDSprovider
# File name of floor plan img
floor_plan_img: <image_name>
# (train, val, test) test=0.2 => 5 fold # The number of temporal epochs into which the dataset is split
split_ratio: [0.7, 0.1, 0.2]
#
# are used when not locally set for pipeline
#
global_params:
# number of experiment repetitions
repetitions: 1
preprocessing:
# Whether to standardize the RSS values
standardize: True
# Whether to assign labels with no matching area to closet area
assign_closest: False
# The floor number of the Lohan dataset, which should be used
floor: 0
# The epoch number of the split dataset
#num_epochs: 10
#epoch: 5
# How to check for area matches of labels (to label positions with matching areas)
area_assignment: convex_hull
grid_size: 40
floor_plan:
# 'segmentation' => computes floor plan segmentation,
# 'regression' => uses DBSCAN to partitions labels into train, test split
type: regression
# file where segmentation is stored or loaded from
seg_file: evaluation/lohan_reg_seg
# whether to 'load' or compute and 'store' the segmentation
mode: load # (load/store)
# file location that holds numpy array with walls (rows of form [s_x, s_y, e_x, e_y]
walls_file: lohan/LohanWalls1stfloor.npy
# parameters of floor plan segmentation algorithm
segmentation:
# algorithm identifier (currently only LDCE supported)
type: LDCE
# file location of precomputed distance file (will be recomputed if it does not exist yet)
precomp_dist_file: evaluation/precomp_dist_lohan(0,0).npy
# Parameters of LDCE algorithm
stop_size: 1
eps: 0.8
max_eps: 50
min_pts: 2
min_members: 1
rss_penalty: 0
wall_penalty: 0
cluster_penalty: 20
area_mode: convex_hull
model_params:
type: GRID_ROT-BBOX # (DNN, CNN, kNN, SVM) supported (require different parameters)
first_neuron: 512
hidden_layers: 1
lr: 0.7
batch_size: 32
epochs: 200
dropout: 0.5
regularization_penalty: 0.0
augmentation: 1
loss:
grid:
scale: 20.0
outside:
scale: 1.0
delta: 20.0
#
# List of pipelines that are executed
#
pipelines:
- name: BBOX_1l_20_aug
# base directories for file storage
output:
model_dir: evaluation/lohan/rot/output/
summary_dir: evaluation/lohan/rot/summary/
img_folder: evaluation/lohan/ # folder where floorplan images is located (if not present, will be downloaded)
\ No newline at end of file
......@@ -136,7 +136,7 @@ def execute(conf_file):
if "grid_size" in pre_params:
data_provider.transform_to_2dim_3layer_grid_encoding(
grid_size=pre_params['grid_size'])
elif "GRID_OVERLAP" in model_params['type']:
elif "GRID_OVERLAP" in model_params['type'] or "GRID_ROT" in model_params['type']:
if "grid_size" in pre_params:
data_provider.transform_to_2dim_overlapping_grid_encoding(
grid_size=pre_params['grid_size'])
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment