Created
January 12, 2021 16:22
-
-
Save keesschollaart81/83de609f0852670656290fe0180da318 to your computer and use it in GitHub Desktop.
Yolov5s TF Lite, outputs parsed in C#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.Extensions.Logging; | |
using Shared; | |
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Diagnostics; | |
var outputTensors = new List<OutputTensor> | |
{ | |
new OutputTensor(0.14855362474918365, 197, (1, 3, 1600, 8), new[] { (10, 13), (16, 30), (33, 23) }, "yolov5_output_tensor_0.bin"), | |
new OutputTensor(0.13808976113796234, 198, (1, 3, 400, 8), new[] { (30, 61), (62,45), (59, 119) }, "yolov5_output_tensor_1.bin"), | |
new OutputTensor(0.14308157563209534, 192, (1, 3, 100, 8), new[] { (116, 90), (156, 198), (373, 326) }, "yolov5_output_tensor_2.bin") | |
}; | |
using ILoggerFactory loggerFactory = LoggerFactory.Create(builder => builder.AddConsole()); | |
ILogger logger = loggerFactory.CreateLogger<BoundingBox>(); | |
Console.WriteLine("Hello World!"); | |
var classesCount = 3; | |
var modelInputRes = (320, 320); | |
var iouThres = 0.5f; | |
var postProcesssedResults = new List<float[]>(); | |
foreach (var outputTensor in outputTensors) | |
{ | |
logger.LogInformation("File: {file}", outputTensor.file); | |
var bytes = File.ReadAllBytes(outputTensor.file); | |
var pred = bytes.Select(b => (b - outputTensor.Zero) * outputTensor.Scale).ToArray(); | |
var ny_nx = outputTensor.Shape.Item3; // 1600 | |
var ratio = (float)modelInputRes.Item1 / modelInputRes.Item2; | |
var nx = (int)Math.Sqrt(ny_nx / ratio); | |
var ny = (int)(ratio * nx); | |
var stride = (int)Math.Floor((float)modelInputRes.Item1 / ny); | |
for (int boxY = 0; boxY < ny; boxY++) | |
{ | |
for (int boxX = 0; boxX < nx; boxX++) | |
{ | |
for (var a = 0; a < outputTensor.Anchors.Length; a++) | |
{ | |
var anchorOffset = ny_nx * a * (classesCount + 5); | |
var yOffset = anchorOffset + (boxY * ny * (classesCount + 5)); | |
var offset = yOffset + (boxX * (classesCount + 5)); | |
var rv = pred[offset..(offset + classesCount + 5)]; | |
var predConf = Sigmoid(rv[4]); | |
if (predConf < 0.25) continue; | |
var predBbox = rv.Select(Sigmoid).ToArray(); | |
var predXywh = predBbox[0..4]; | |
var predProb = predBbox.Skip(5).ToArray(); | |
var rawDx = predXywh[0]; | |
var rawDy = predXywh[1]; | |
var rawDw = predXywh[2]; | |
var rawDh = predXywh[3]; | |
float predX = ((rawDx * 2f) - 0.5f + boxX) * stride; | |
float predY = ((rawDy * 2f) - 0.5f + boxY) * stride; | |
var pwr = (float)Math.Pow(rawDw * 2, 2); | |
float predW = pwr * outputTensor.Anchors[a].Item1; | |
float predH = (float)Math.Pow(rawDh * 2, 2) * outputTensor.Anchors[a].Item2; | |
// postprocess_boxes | |
// (1) (x, y, w, h) --> (xmin, ymin, xmax, ymax) | |
var box = Xywh2xyxy(new float[] { predX, predY, predW, predH }); | |
float predX1 = box[0]; //predX - predW * 0.5f; | |
float predY1 = box[1]; //predY - predH * 0.5f; | |
float predX2 = box[2]; //predX + predW * 0.5f; | |
float predY2 = box[3]; //predY + predH * 0.5f; | |
// (2) (xmin, ymin, xmax, ymax) -> (xmin_org, ymin_org, xmax_org, ymax_org) | |
float org_h = modelInputRes.Item1; | |
float org_w = modelInputRes.Item2; | |
float inputSize = 320f; | |
float resizeRatio = Math.Min(inputSize / org_w, inputSize / org_h); | |
float dw = (inputSize - resizeRatio * org_w) / 2f; | |
float dh = (inputSize - resizeRatio * org_h) / 2f; | |
float orgX1 = 1f * (predX1 - dw) / resizeRatio; // left | |
float orgX2 = 1f * (predX2 - dw) / resizeRatio; // right | |
float orgY1 = 1f * (predY1 - dh) / resizeRatio; // top | |
float orgY2 = 1f * (predY2 - dh) / resizeRatio; // bottom | |
// (3) clip some boxes that are out of range | |
orgX1 = Math.Max(orgX1, 0); | |
orgY1 = Math.Max(orgY1, 0); | |
orgX2 = Math.Min(orgX2, org_w - 1); | |
orgY2 = Math.Min(orgY2, org_h - 1); | |
if (orgX1 > orgX2 || orgY1 > orgY2) continue; // invalid_mask | |
// (5) discard some boxes with low scores | |
var scores = predProb.Select(p => p * predConf).ToList(); | |
float scoreMaxCat = scores.Max(); | |
if (scoreMaxCat > 0.25) | |
{ | |
logger.LogInformation("Class: {class} Score: {score} {orgX1:0.} {orgY1:0.} {orgX2:0.} {orgY2:0.}", scores.IndexOf(scoreMaxCat), scoreMaxCat, orgX1, orgY1, orgX2, orgY2); | |
postProcesssedResults.Add(new float[] { orgX1, orgY1, orgX2, orgY2, scoreMaxCat, scores.IndexOf(scoreMaxCat) }); | |
} | |
} | |
} | |
} | |
} | |
postProcesssedResults = postProcesssedResults.OrderByDescending(x => x[4]).ToList(); // sort by confidence | |
int f = 0; | |
while (f < postProcesssedResults.Count) | |
{ | |
var res = postProcesssedResults[f]; | |
if (res == null) | |
{ | |
f++; | |
continue; | |
} | |
var conf = res[4]; | |
logger.LogInformation("End: Class: {class} Score: {score}", (int)res[5], conf); | |
postProcesssedResults[f] = null; | |
var iou = postProcesssedResults.Select(bbox => bbox == null ? float.NaN : BoxIoU(res, bbox)).ToList(); | |
for (int j = 0; j < iou.Count; j++) | |
{ | |
if (float.IsNaN(iou[j])) continue; | |
if (iou[j] > iouThres) | |
{ | |
postProcesssedResults[j] = null; // deactivated for debugging | |
} | |
} | |
f++; | |
} | |
static float BoxIoU(float[] boxes1, float[] boxes2) | |
{ | |
static float box_area(float[] box) | |
{ | |
return (box[2] - box[0]) * (box[3] - box[1]); | |
} | |
var area1 = box_area(boxes1); | |
var area2 = box_area(boxes2); | |
Debug.Assert(area1 >= 0); | |
Debug.Assert(area2 >= 0); | |
var dx = Math.Max(0, Math.Min(boxes1[2], boxes2[2]) - Math.Max(boxes1[0], boxes2[0])); | |
var dy = Math.Max(0, Math.Min(boxes1[3], boxes2[3]) - Math.Max(boxes1[1], boxes2[1])); | |
var inter = dx * dy; | |
return inter / (area1 + area2 - inter); | |
} | |
static float[] Xywh2xyxy(float[] bbox) | |
{ | |
var bboxAdj = new float[4]; | |
bboxAdj[0] = bbox[0] - bbox[2] / 2f; | |
bboxAdj[1] = bbox[1] - bbox[3] / 2f; | |
bboxAdj[2] = bbox[0] + bbox[2] / 2f; | |
bboxAdj[3] = bbox[1] + bbox[3] / 2f; | |
return bboxAdj; | |
} | |
static float Sigmoid(double value) | |
{ | |
return 1.0f / (1.0f + (float)Math.Exp(-value)); | |
} | |
public record OutputTensor(double Scale, int Zero, (int, int, int, int) Shape, (int, int)[] Anchors, string file); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment