SSD Mobilenet V2 FPNLite 320x320 not working with TFLite

Hi,

I’ve trained an SSD Mobilienet model with Tensorflow 2.3.1 that I want to test on TFLite. The model works perfectly when doing inference normally, but it suddenly loses all precision when converted to TFLite. I get no detections if I turn the detection threshold above 3%.

How it should look:

How it looks:

I leave here the script I use for conversion and for inference:
Conversion

from lib2to3.pytree import convert
from operator import mod
import os
import shutil
import subprocess

import tensorflow as tf

def compress(model):
    # Convert the model
    converter = tf.lite.TFLiteConverter.from_saved_model("output/exported/"+model+"/saved_model") # path to the SavedModel directory
    tflite_model = converter.convert()

    # Save the model.
    with open('output/compressed/'+model+'.tflite', 'wb') as f:
        f.write(tflite_model)


input = [ f.path.split("/")[-1] for f in os.scandir('input') if f.is_dir() ]

folders = ['output/exported', 'output/compressed', 'output/coral']
for folder in folders:
    for filename in os.listdir(folder):
        file_path = os.path.join(folder, filename)
        try:
            if os.path.isfile(file_path) or os.path.islink(file_path):
                os.unlink(file_path)
            elif os.path.isdir(file_path):
                shutil.rmtree(file_path)
        except Exception as e:
            print('Failed to delete %s. Reason: %s' % (file_path, e))

for model in input:
    print(model)
    subprocess.run(["python3", "export_tflite_graph_tf2.py", "--pipeline_config_path", 
        "input/"+model+"/pipeline.config", "--trained_checkpoint_dir", "input/"+model+"/checkpoint/",
        "--output_directory", "output/exported/"+model], shell=False)

    compress(model)
    subprocess.run(["edgetpu_compiler", "output/compressed/"+model+".tflite", "--out_dir", "output/coral"])

Inference:

# based on https://github.com/tensorflow/examples/blob/master/lite/examples/object_detection/raspberry_pi/detect_picamera.py
from asyncio import sleep
from unicodedata import name
from tflite_runtime.interpreter import Interpreter, load_delegate
import argparse
import time
import cv2
import re
import numpy as np
import os
from os import listdir
from os.path import isfile, join
import sys
import datetime


def draw_detection(frame, results, path):
    size = frame.shape
    # print("RESULTS: ",results)
    for idx, obj in enumerate(results):
        # print(obj)

        # Prepare boundary box
        ymin, xmin, ymax, xmax = obj['bounding_box']
        xmin = int(xmin * size[1])
        xmax = int(xmax * size[1])
        ymin = int(ymin * size[0])
        ymax = int(ymax * size[0])
        cv2.rectangle(frame, (xmin, ymax), (xmax, ymin), (255,0,0), 2)

    name_og = str(datetime.datetime.now()) + ".jpg"
    print(os.path.join(path, name_og))
    cv2.imwrite(os.path.join(path, name_og), frame)

def load_labels(path):
    """Loads the labels file. Supports files with or without index numbers."""
    with open(path, 'r', encoding='utf-8') as f:
        lines = f.readlines()
        labels = {}
        for row_number, content in enumerate(lines):
            pair = re.split(r'[:\s]+', content.strip(), maxsplit=1)
            if len(pair) == 2 and pair[0].strip().isdigit():
                labels[int(pair[0])] = pair[1].strip()
            else:
                labels[row_number] = pair[0].strip()
    return labels


def set_input_tensor(interpreter, image):
    """Sets the input tensor."""
    tensor_index = interpreter.get_input_details()[0]['index']
    input_tensor = interpreter.tensor(tensor_index)()[0]
    input_tensor[:, :] = image


def get_output_tensor(interpreter, index):
    """Returns the output tensor at the given index."""
    output_details = interpreter.get_output_details()[index]
    tensor = np.squeeze(interpreter.get_tensor(output_details['index']))
    return tensor


def detect_objects(interpreter, image, threshold):
    """Returns a list of detection results, each a dictionary of object info."""
    set_input_tensor(interpreter, image)
    interpreter.invoke()

    # RESNET DADO
    boxes = get_output_tensor(interpreter, 1)
    classes = get_output_tensor(interpreter, 3)
    scores = get_output_tensor(interpreter, 0)
    count = int(get_output_tensor(interpreter, 2))

    # RESNET LH
    
    # boxes = get_output_tensor(interpreter, 0)
    # classes = get_output_tensor(interpreter, 3)
    # scores = get_output_tensor(interpreter, 1)
    # count = int(get_output_tensor(interpreter, 2))

    '''
    print('BOXES', boxes)
    print('CLASSES', classes)
    print('SCORES', scores)
    print('COUNT', count)
    '''
    

    results = []
    for i in range(count):
        if scores[i] >= threshold:
            result = {
                'bounding_box': boxes[i],
                'class_id': classes[i],
                'score': scores[i]
            }
            results.append(result)
    return results


def make_interpreter(model_file, use_edgetpu):
    model_file, *device = model_file.split('@')
    if use_edgetpu:
        return Interpreter(
            model_path=model_file,
            experimental_delegates=[
                load_delegate('libedgetpu.so.1',
                {'device': device[0]} if device else {})
            ]
        )
    else:
        return Interpreter(model_path=model_file)


def main():
    parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument('-m', '--model', type=str, required=True, help='File path of .tflite file.')
    parser.add_argument('-l', '--labels', type=str, required=True, help='File path of labels file.')
    parser.add_argument('-i', '--input', type=str, required=True, help='File path of images dir.')
    parser.add_argument('-o', '--output', type=str, required=True, help='File path of output dir.')
    parser.add_argument('-n', '--num', type=int, required=True, help='Number of iterations.')
    parser.add_argument('-t', '--threshold', type=float, default=0.4, required=False, help='Score threshold for detected objects.')
    parser.add_argument('-e', '--use_edgetpu', action='store_true', default=False, help='Use EdgeTPU')
    args = parser.parse_args()

    labels = load_labels(args.labels)
    interpreter = make_interpreter(args.model, args.use_edgetpu)
    interpreter.allocate_tensors()
    _, input_height, input_width, _ = interpreter.get_input_details()[0]['shape']

    print("SIZES: ", input_height, " , ", input_width)

    img_list = [f for f in listdir(args.input) if isfile(join(args.input, f))]
    list_len = len(img_list)

    for i in range(args.num):
        sys.stdout.write("\rStep %i..." % i)
        sys.stdout.flush()
        j = i%list_len
        path = args.input + "/" + img_list[j]
        # print(path)
        # Initialize video stream
        frame = cv2.imread(path) 
        frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        frame_resized = cv2.resize(frame_rgb, (input_width, input_height))
        input_data = np.expand_dims(frame_resized, axis=0)

        # Perform inference
        start = time.time()
        results = detect_objects(interpreter, input_data, args.threshold)
        end = time.time()
        print("TIME: ", end - start)
        draw_detection(frame, results, args.output)


if __name__ == '__main__':
    main()

Any idea on what might be happening? Thanks.