文章思路
大神的github和个人网站已经对此进行了说明,这里不再赘述。。。
源码理解
一. 标签点形式
- 给四个点排列顺序制定规则
'''
按顺序排列四个点,逆时针旋转,且第一个点为左上角点(刚开始选择最左边的点,
如果最后计算的第二个点的Y比第一个点大,那就让最后一个点做为第一个点,其他点依次右移)
-
1.以最小的X坐标为起点(起名为A)
-
2.其他三个点和第一个点(A)连线形成夹角,取中间的点为第三个点(起名C)
-
3.以AC为连线,在AC上方为D,下方为B
-
4.最后比较AC和BD的斜率,AC>BD ===> 顺序调整为DABC AC<BD ===> 维持ABCD
-
5.感觉第四步没啥意义,只要是顺序就好了,没必要那么苛刻。。。。
'''
- 下面给出一些例子
- 注意长边的位置
针对上面两幅图,第一张的long_edge=0,2
,第二张的long_edge=1,3
二. 标签切边
- 以最短边的0.3进行缩放当做内部点
- 以最短边的0.6作为头尾点
注意:这里头尾都是针对最长边上的操作
注意:头和尾是按照标签点的顺序进行的,排在前面为头,排在后面为尾
三. loss计算
这部分比较简单,建议由需要的读者直接读取一个data
进行debug即可:
#input : 1*w*h*3
#label : 1*160*160*7(batch,w,h,type)
def quad_loss(y_true, y_pred):
# loss for inside_score
logits = y_pred[:, :, :, :1]
labels = y_true[:, :, :, :1]
# balance positive and negative samples in an image
beta = 1 - tf.reduce_mean(labels)
# first apply sigmoid activation
predicts = tf.nn.sigmoid(logits)
# log +epsilon for stable cal
inside_score_loss = tf.reduce_mean(
-1 * (beta * labels * tf.log(predicts + cfg.epsilon) +
(1 - beta) * (1 - labels) * tf.log(1 - predicts + cfg.epsilon)))
inside_score_loss *= cfg.lambda_inside_score_loss
# loss for side_vertex_code
vertex_logits = y_pred[:, :, :, 1:3]
vertex_labels = y_true[:, :, :, 1:3]
vertex_beta = 1 - (tf.reduce_mean(y_true[:, :, :, 1:2])
/ (tf.reduce_mean(labels) + cfg.epsilon))
vertex_predicts = tf.nn.sigmoid(vertex_logits)
pos = -1 * vertex_beta * vertex_labels * tf.log(vertex_predicts +
cfg.epsilon)
neg = -1 * (1 - vertex_beta) * (1 - vertex_labels) * tf.log(
1 - vertex_predicts + cfg.epsilon)
positive_weights = tf.cast(tf.equal(y_true[:, :, :, 0], 1), tf.float32)
side_vertex_code_loss =
tf.reduce_sum(tf.reduce_sum(pos + neg, axis=-1) * positive_weights) / (
tf.reduce_sum(positive_weights) + cfg.epsilon)
side_vertex_code_loss *= cfg.lambda_side_vertex_code_loss
# loss for side_vertex_coord delta
g_hat = y_pred[:, :, :, 3:]
g_true = y_true[:, :, :, 3:]
vertex_weights = tf.cast(tf.equal(y_true[:, :, :, 1], 1), tf.float32)
pixel_wise_smooth_l1norm = smooth_l1_loss(g_hat, g_true, vertex_weights)
side_vertex_coord_loss = tf.reduce_sum(pixel_wise_smooth_l1norm) / (
tf.reduce_sum(vertex_weights) + cfg.epsilon)
side_vertex_coord_loss *= cfg.lambda_side_vertex_coord_loss
return inside_score_loss + side_vertex_code_loss + side_vertex_coord_loss
def smooth_l1_loss(prediction_tensor, target_tensor, weights):
n_q = tf.reshape(quad_norm(target_tensor), tf.shape(weights))
diff = prediction_tensor - target_tensor
abs_diff = tf.abs(diff)
abs_diff_lt_1 = tf.less(abs_diff, 1)
pixel_wise_smooth_l1norm = (tf.reduce_sum(
tf.where(abs_diff_lt_1, 0.5 * tf.square(abs_diff), abs_diff - 0.5),
axis=-1) / n_q) * weights
return pixel_wise_smooth_l1norm
def quad_norm(g_true):
shape = tf.shape(g_true)
delta_xy_matrix = tf.reshape(g_true, [-1, 2, 2])
diff = delta_xy_matrix[:, 0:1, :] - delta_xy_matrix[:, 1:2, :]
square = tf.square(diff)
distance = tf.sqrt(tf.reduce_sum(square, axis=-1))
distance *= 4.0
distance += cfg.epsilon
return tf.reshape(distance, shape[:-1])
if __name__ == '__main__':
x, y = data_generator.gen(1)
loss_t = quad_loss(y,y)
四. NMS
这部分没仔细看,传统的NMS和LNMS都比较简单,大概看一下就好了
这里主要是说明一下几个参数:
pixel_threshold = 0.9 #内部点阈值(目标点概率)
side_vertex_pixel_threshold = 0.9 #内部头尾点的阈值
##头尾点取值范围,head->[0,trunc_threshold] tail->[1-trunc_threshold,1],变大之后检测能力变强
trunc_threshold = 0.1
最后说明
其实这个项目的思路很简单,看一下就明白,但是具体实现还是有点棘手,难点在于标签的制作
边界点负责回归边界,这个边界如何确定?如何确定头和尾?
具体代码的注释写在里面了,还有很多小细节看笔者注释即可
下载地址