oarust 0.8.6

GPU-first semantic computing engine written in Rust
import storage;
import attributes;

struct PushConstants {
	uint predicted_boxes_index;
	uint predicted_scores_index;
	uint predicted_classes_index;
	uint predicted_images_index;
	uint target_boxes_index;
	uint target_classes_index;
	uint target_images_index;
	uint thresholds_index;
	uint state_index;
	uint curve_index;
	uint counts_index;
	uint metrics_index;
	uint predicted_count;
	uint target_count;
	uint threshold_count;
	uint class_count;
	uint state_stride;
	float score_threshold;
};
[[vk::push_constant]] PushConstants push;
[[vk::binding(0, 0)]] RWByteAddressBuffer storage_buffers[];

float box_iou(uint predicted_row, uint target_row) {
	uint a = predicted_row * 4u;
	uint b = target_row * 4u;
	float a_center_x = load_f32(storage_buffers[push.predicted_boxes_index], a);
	float a_center_y = load_f32(storage_buffers[push.predicted_boxes_index], a + 1u);
	float a_width = load_f32(storage_buffers[push.predicted_boxes_index], a + 2u);
	float a_height = load_f32(storage_buffers[push.predicted_boxes_index], a + 3u);
	float b_center_x = load_f32(storage_buffers[push.target_boxes_index], b);
	float b_center_y = load_f32(storage_buffers[push.target_boxes_index], b + 1u);
	float b_width = load_f32(storage_buffers[push.target_boxes_index], b + 2u);
	float b_height = load_f32(storage_buffers[push.target_boxes_index], b + 3u);
	if (!isfinite(a_center_x) || !isfinite(a_center_y) || !isfinite(a_width) ||
		!isfinite(a_height) || !isfinite(b_center_x) || !isfinite(b_center_y) ||
		!isfinite(b_width) || !isfinite(b_height)) return 0.0f;
	a_width = max(a_width, 0.0f);
	a_height = max(a_height, 0.0f);
	b_width = max(b_width, 0.0f);
	b_height = max(b_height, 0.0f);
	float intersection = max(
		min(a_center_x + a_width * 0.5f, b_center_x + b_width * 0.5f) -
		max(a_center_x - a_width * 0.5f, b_center_x - b_width * 0.5f),
		0.0f) * max(
		min(a_center_y + a_height * 0.5f, b_center_y + b_height * 0.5f) -
		max(a_center_y - a_height * 0.5f, b_center_y - b_height * 0.5f),
		0.0f);
	float union_area = a_width * a_height + b_width * b_height - intersection;
	return isfinite(union_area) && union_area > 0.0f ? intersection / union_area : 0.0f;
}

[kernel_name("detection_metric_curves")]
[domain("vision")]
[variant("greedy_score_rank")]
[dtype("f32")]
[status("experimental")]
[shader("compute")]
[numthreads(1, 1, 1)]
void main(uint3 thread_id : SV_DispatchThreadID) {
	uint pair = thread_id.x;
	uint pair_count = push.threshold_count * push.class_count;
	if (pair >= pair_count) return;
	uint threshold_index = pair / push.class_count;
	uint class_index = pair - threshold_index * push.class_count;
	float iou_threshold = load_f32(storage_buffers[push.thresholds_index], threshold_index);
	uint state_base = pair * push.state_stride;
	uint curve_base = pair * push.predicted_count * 2u;
	for (uint index = 0; index < push.state_stride; ++index) {
		store_u32(storage_buffers[push.state_index], state_base + index, 0u);
	}
	for (uint index = 0; index < push.predicted_count * 2u; ++index) {
		store_f32(storage_buffers[push.curve_index], curve_base + index, 0.0f);
	}

	uint target_count_for_class = 0u;
	for (uint target = 0; target < push.target_count; ++target) {
		if (load_i32(storage_buffers[push.target_classes_index], target) == int(class_index)) {
			target_count_for_class++;
		}
	}
	uint true_positive = 0u;
	uint false_positive = 0u;
	uint evaluated_true_positive = 0u;
	uint evaluated_false_positive = 0u;
	uint rank_count = 0u;
	for (uint rank = 0; rank < push.predicted_count; ++rank) {
		uint best_prediction = 0xffffffffu;
		float best_score = -3.402823466e+38f;
		for (uint candidate = 0; candidate < push.predicted_count; ++candidate) {
			if (load_u32(storage_buffers[push.state_index], state_base + candidate) != 0u) continue;
			if (load_i32(storage_buffers[push.predicted_classes_index], candidate) != int(class_index)) continue;
			float score = load_f32(storage_buffers[push.predicted_scores_index], candidate);
			if (!isfinite(score)) continue;
			if (score > best_score || (score == best_score && candidate < best_prediction)) {
				best_prediction = candidate;
				best_score = score;
			}
		}
		if (best_prediction == 0xffffffffu) break;
		store_u32(storage_buffers[push.state_index], state_base + best_prediction, 1u);

		uint best_target = 0xffffffffu;
		float best_iou = -1.0f;
		int predicted_image = load_i32(storage_buffers[push.predicted_images_index], best_prediction);
		for (uint target = 0; target < push.target_count; ++target) {
			if (load_u32(storage_buffers[push.state_index], state_base + push.predicted_count + target) != 0u) continue;
			if (load_i32(storage_buffers[push.target_classes_index], target) != int(class_index) ||
				load_i32(storage_buffers[push.target_images_index], target) != predicted_image) continue;
			float candidate_iou = box_iou(best_prediction, target);
			if (candidate_iou > best_iou || (candidate_iou == best_iou && target < best_target)) {
				best_target = target;
				best_iou = candidate_iou;
			}
		}
		bool matched = best_target != 0xffffffffu && isfinite(iou_threshold) &&
			iou_threshold >= 0.0f && iou_threshold <= 1.0f && best_iou >= iou_threshold;
		if (matched) {
			store_u32(storage_buffers[push.state_index], state_base + push.predicted_count + best_target, 1u);
			true_positive++;
		} else {
			false_positive++;
		}
		if (best_score >= push.score_threshold) {
			if (matched) evaluated_true_positive++;
			else evaluated_false_positive++;
		}
		float precision = float(true_positive) / float(true_positive + false_positive);
		float recall = target_count_for_class > 0u ? float(true_positive) / float(target_count_for_class) : 0.0f;
		store_f32(storage_buffers[push.curve_index], curve_base + rank_count * 2u, precision);
		store_f32(storage_buffers[push.curve_index], curve_base + rank_count * 2u + 1u, recall);
		rank_count++;
	}
	uint false_negative = target_count_for_class > evaluated_true_positive ?
		target_count_for_class - evaluated_true_positive : 0u;
	uint counts_base = pair * 3u;
	store_u32(storage_buffers[push.counts_index], counts_base, evaluated_true_positive);
	store_u32(storage_buffers[push.counts_index], counts_base + 1u, evaluated_false_positive);
	store_u32(storage_buffers[push.counts_index], counts_base + 2u, false_negative);
	float precision = evaluated_true_positive + evaluated_false_positive > 0u ?
		float(evaluated_true_positive) / float(evaluated_true_positive + evaluated_false_positive) : 0.0f;
	float recall = target_count_for_class > 0u ?
		float(evaluated_true_positive) / float(target_count_for_class) : 0.0f;
	float f1 = precision + recall > 0.0f ? 2.0f * precision * recall / (precision + recall) : 0.0f;
	uint metrics_base = pair * 4u;
	store_f32(storage_buffers[push.metrics_index], metrics_base, precision);
	store_f32(storage_buffers[push.metrics_index], metrics_base + 1u, recall);
	store_f32(storage_buffers[push.metrics_index], metrics_base + 2u, f1);
	store_f32(storage_buffers[push.metrics_index], metrics_base + 3u, 0.0f);
}