hhk7734 / tensorflow-yolov4

YOLOv4 Implemented in Tensorflow 2.
MIT License
136 stars 75 forks source link

Non-maximum supression and conversion #36

Closed Eric112752 closed 4 years ago

Eric112752 commented 4 years ago

Hi, thank you for created such a beautiful package.

I have trouble in converting Yolov4 tiny from weights to tflite with int8 quantization, but I don't know how to use the parameter in data_set in the function save_as_tflite.

Besides, I used the tflite float16 yolov4-tiny but it showed a lot of bounding boxes, can you suggest for me how to adding the non-maximum suppression so that the bounding box would be more accurate?

Thank you a lot.

hhk7734 commented 4 years ago

Tested on

ref:

from yolov4.tf import YOLOv4

yolo = YOLOv4(tiny=True)
yolo.classes = "coco.names"

yolo.make_model()
yolo.load_weights("yolov4-tiny.weights", weights_type="yolo")

dataset = yolo.load_dataset(
    "train2017.txt",
    training=False,
    image_path_prefix=r"D:\coco\train2017"
)

yolo.save_as_tflite(
    "yolov4-tiny.tflite",
    quantization="full_int8",
    data_set=dataset,
    num_calibration_steps=500
)

yolov4-tiny-int8.tflite: yolov4-tiny.zip leaky-relu for tflite is in TF 2.3.1(?) or higher. https://github.com/tensorflow/tensorflow/pull/36876


NMS is in yolo.candidates_to_pred_bboxes

https://github.com/hhk7734/tensorflow-yolov4/blob/1149d7645a15c7600388fdc3af708b00505922af/py_src/yolov4/tflite/__init__.py#L73-L114

Eric112752 commented 4 years ago

Hi, I currently using predict function combine with pygame to updating the frame in real-time, but when I increase the iou to above 0.5, the frame crash and it stop predicting, can you help me out

from yolov4.tflite import YOLOv4
import tensorflow as tf
import cv2
from time import sleep
from PIL import Image, ImageFont, ImageDraw
import pygame
import os
import colorsys
import time
import numpy as np
from keras import backend as K
from keras.models import load_model
from keras.layers import Input

from PIL import Image, ImageFont, ImageDraw
from timeit import default_timer as timer
import matplotlib.pyplot as plt
import cv2
h,w=480,680
border=50
N=0

def getFrame():
    """Generate next frame of simulation as numpy array"""

    # Create data on first call only
    if getFrame.z is None:
        xx, yy = np.meshgrid(np.linspace(0,2*np.pi,w), np.linspace(0,2*np.pi,h))
        getFrame.z = sin2d(xx, yy)
        getFrame.z = cv2.normalize(getFrame.z,None,alpha=0,beta=1,norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_32F)

    # Just roll data for subsequent calls
    getFrame.z = np.roll(getFrame.z,(1,2),(0,1))
    return getFrame.z
import cv2
from time import sleep
key = cv2.waitKey(1)
webcam = cv2.VideoCapture(0)
sleep(2)
frame_count = 0

pygame.init()
screen = pygame.display.set_mode((w+(2*border), h+(2*border)))
pygame.display.set_caption("Yolov4- Test")
done = False
clock = pygame.time.Clock()
prev_time = 0
basicfont = pygame.font.SysFont(None, 32)
if __name__ == '__main__':
    yolo = YOLOv4(tiny = True)

    yolo.classes = "custom.names"
    #yolo.make_model()
    #yolo.load_weights("yolov4-tiny.weights", weights_type="yolo")
    yolo.load_tflite("detect.tflite")
    while True:
        try:

            check, frame = webcam.read()
            print(check) #prints true as long as the webcam is running
            print(frame) #prints matrix values of each framecd

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    done = True

            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            img_ = frame
            _bboxes = yolo.predict(img_, iou_threshold=0.5, score_threshold=0.25)
            curr_time = time.time()

            getFrame.z= yolo.draw_bboxes(img_, _bboxes)
            npimage= getFrame()

            # Clear screen to white before drawing 
            screen.fill((255, 255, 255))
            # Convert to a surface and splat onto screen offset by border width and height
            surface = pygame.surfarray.make_surface(npimage)
            surface = pygame.transform.rotate(surface, 270)
            surface = pygame.transform.flip(surface, True, False)
            screen.blit(surface, (border, border))

            # Display and update frame counter
            text = basicfont.render('FPS: ' + str(round(N,2)), True, (255, 0, 0), (255, 255, 255))
            screen.blit(text, (border,h+border))
            N = 1 / (curr_time - prev_time)

            pygame.display.flip()
            clock.tick(10)
            prev_time = curr_time
            if key == ord('q'):
                webcam.release()
                cv2.destroyAllWindows()
                break
        except(KeyboardInterrupt):
            print("Turning off camera.")
            webcam.release()
            print("Camera off.")
            print("Program ended.")
            cv2.destroyAllWindows()
            break
hhk7734 commented 4 years ago

As the iou_threshold is lower, the number of duplicated boxes decreases.

Eric112752 commented 4 years ago

okay thank you a lot, but in my program when I increase the iou_threshold, the program crashed and stopped predicting, can you testing out the code? Or maybe are there anyway inferencing yolov4 tiny using webcam in real time

hhk7734 commented 4 years ago
from yolov4.tf import YOLOv4
import tensorflow as tf
import cv2
from time import sleep
from PIL import Image, ImageFont, ImageDraw
import pygame
import os
import colorsys
import time
import numpy as np

from PIL import Image, ImageFont, ImageDraw
from timeit import default_timer as timer
import matplotlib.pyplot as plt
import cv2
h,w=480,680
border=50
N=0

def getFrame():
    """Generate next frame of simulation as numpy array"""

    # Create data on first call only
    if getFrame.z is None:
        xx, yy = np.meshgrid(np.linspace(0,2*np.pi,w), np.linspace(0,2*np.pi,h))
        getFrame.z = sin2d(xx, yy)
        getFrame.z = cv2.normalize(getFrame.z,None,alpha=0,beta=1,norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_32F)

    # Just roll data for subsequent calls
    getFrame.z = np.roll(getFrame.z,(1,2),(0,1))
    return getFrame.z
import cv2
from time import sleep
key = cv2.waitKey(1)
webcam = cv2.VideoCapture(0)
sleep(2)
frame_count = 0

pygame.init()
screen = pygame.display.set_mode((w+(2*border), h+(2*border)))
pygame.display.set_caption("Yolov4- Test")
done = False
clock = pygame.time.Clock()
prev_time = 0
basicfont = pygame.font.SysFont(None, 32)
if __name__ == '__main__':
    yolo = YOLOv4(tiny = True)

    yolo.classes = "custom.names"
    yolo.make_model()
    yolo.load_weights("yolov4-tiny.weights", weights_type="yolo")
    while True:
        try:

            check, frame = webcam.read()
            print(check) #prints true as long as the webcam is running
            print(frame) #prints matrix values of each framecd

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    done = True

            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            img_ = frame
            _bboxes = yolo.predict(img_, iou_threshold=0.1, score_threshold=0.25)
            curr_time = time.time()

            getFrame.z= yolo.draw_bboxes(img_, _bboxes)
            npimage= getFrame()

            # Clear screen to white before drawing 
            screen.fill((255, 255, 255))
            # Convert to a surface and splat onto screen offset by border width and height
            surface = pygame.surfarray.make_surface(npimage)
            surface = pygame.transform.rotate(surface, 270)
            surface = pygame.transform.flip(surface, True, False)
            screen.blit(surface, (border, border))

            # Display and update frame counter
            text = basicfont.render('FPS: ' + str(round(N,2)), True, (255, 0, 0), (255, 255, 255))
            screen.blit(text, (border,h+border))
            N = 1 / (curr_time - prev_time)

            pygame.display.flip()
            clock.tick(10)
            prev_time = curr_time
            if key == ord('q'):
                webcam.release()
                cv2.destroyAllWindows()
                break
        except(KeyboardInterrupt):
            print("Turning off camera.")
            webcam.release()
            print("Camera off.")
            print("Program ended.")
            cv2.destroyAllWindows()
            break

it works well.

hhk7734 commented 4 years ago

On PC, tflite is slower than tf.(I tested on CPU only)

your script works like yolo.inference(0, is_image=False)

Eric112752 commented 4 years ago

I did it but

Traceback (most recent call last): File "C:\Users\Son\Downloads\RIVER_SAVER\yolov4-tiny\webcam.py", line 8, in yolo.inference(0, is_image=False) File "C:\Users\Son\AppData\Local\Programs\Python\Python36\lib\site-packages\yolov4\common\base_class.py", line 235, in inference raise FileNotFoundError("{} does not exist".format(media_path)) FileNotFoundError: 0 does not exist

Eric112752 commented 4 years ago

Tested on

  • TF: v2.2.1
  • yolov4: v1.2.1

ref:

from yolov4.tf import YOLOv4

yolo = YOLOv4(tiny=True)
yolo.classes = "coco.names"

yolo.make_model()
yolo.load_weights("yolov4-tiny.weights", weights_type="yolo")

dataset = yolo.load_dataset(
    "train2017.txt",
    training=False,
    image_path_prefix=r"D:\coco\train2017"
)

yolo.save_as_tflite(
    "yolov4-tiny.tflite",
    quantization="full_int8",
    data_set=dataset,
    num_calibration_steps=500
)

yolov4-tiny-int8.tflite: yolov4-tiny.zip leaky-relu for tflite is in TF 2.3.1(?) or higher. tensorflow/tensorflow#36876

NMS is in yolo.candidates_to_pred_bboxes

https://github.com/hhk7734/tensorflow-yolov4/blob/1149d7645a15c7600388fdc3af708b00505922af/py_src/yolov4/tflite/__init__.py#L73-L114

this is the error when I try to convert my yolov4-tiny

WARNING:tensorflow:From C:\Users\Son\AppData\Local\Programs\Python\Python36\lib\site-packages\tensorflow\python\training\tracking\tracking.py:111: Model.state_updates (from tensorflow.python.keras.engine.training) is deprecated and will be removed in a future version. Instructions for updating: This property should not be used in TensorFlow 2.0, as updates are applied automatically. WARNING:tensorflow:From C:\Users\Son\AppData\Local\Programs\Python\Python36\lib\site-packages\tensorflow\python\training\tracking\tracking.py:111: Layer.updates (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version. Instructions for updating: This property should not be used in TensorFlow 2.0, as updates are applied automatically. Traceback (most recent call last): File "C:\Users\Son\Downloads\RIVER_SAVER\yolov4-tiny\quantization_tflite.py", line 17, in <module> num_calibration_steps=200) File "C:\Users\Son\AppData\Local\Programs\Python\Python36\lib\site-packages\yolov4\tf\__init__.py", line 158, in save_as_tflite tflite_model = converter.convert() File "C:\Users\Son\AppData\Local\Programs\Python\Python36\lib\site-packages\tensorflow\lite\python\lite.py", line 831, in convert self).convert(graph_def, input_tensors, output_tensors) File "C:\Users\Son\AppData\Local\Programs\Python\Python36\lib\site-packages\tensorflow\lite\python\lite.py", line 638, in convert result = self._calibrate_quantize_model(result, **flags) File "C:\Users\Son\AppData\Local\Programs\Python\Python36\lib\site-packages\tensorflow\lite\python\lite.py", line 452, in _calibrate_quantize_model inference_output_type, allow_float, activations_type) File "C:\Users\Son\AppData\Local\Programs\Python\Python36\lib\site-packages\tensorflow\lite\python\optimize\calibrator.py", line 98, in calibrate_and_quantize np.dtype(activations_type.as_numpy_dtype()).num) RuntimeError: Unsupported output type INT8 for output tensor 'Identity' of type FLOAT32.

hhk7734 commented 4 years ago

If you used tensorflow v2.3.0 or v2.1.0, uninstall it and install tensorflow==2.2.1.

Eric112752 commented 4 years ago

On PC, tflite is slower than tf.(I tested on CPU only)

your script works like yolo.inference(0, is_image=False)

hi do you know why I showed this error:

Traceback (most recent call last): File "C:\Users\Son\Downloads\RIVER_SAVER\yolov4-tiny\webcam.py", line 8, in yolo.inference(0, is_image=False) File "C:\Users\Son\AppData\Local\Programs\Python\Python36\lib\site-packages\yolov4\common\base_class.py", line 235, in inference raise FileNotFoundError("{} does not exist".format(media_path)) FileNotFoundError: 0 does not exist

Again, you are the saver, thank you a lot!!

hhk7734 commented 4 years ago

I tested on Windows 10 now. it works. Check cam connection.

Eric112752 commented 4 years ago

I tested on Windows 10 now. it works. Check cam connection.

oh I just understand, compile and run won't work but running py will work. Anyway, can you testing out this model, I have just converted it successfully and feel very happy, but I cannot testing it while other model works well, Thank you.

Eric112752 commented 4 years ago

yolov4-tiny.zip

hhk7734 commented 4 years ago

It works, but the performance seems to be poor.

Eric112752 commented 4 years ago

It works, but the performance seems to be poor.

yep, I tested it and it seems to be so slow, even slower than the float16 one, am I wrong somewhere? this is my quantization code `from yolov4.tf import YOLOv4 yolo = YOLOv4(tiny= True)

yolo.classes = "custom.names"

yolo.make_model() yolo.load_weights("yolov4-tiny.weights", weights_type="yolo")

dataset = yolo.load_dataset( "val.txt", training=False, image_path_prefix=r"data/valid" )

yolo.save_as_tflite("yolov4-tiny-int.tflite", quantization="full_int8", data_set=dataset, num_calibration_steps=200)`

Moreover, when using 'inference(0, is_image=False)' when I try to improve the iou_threshold to >0.5 then the program crashed

Eric112752 commented 4 years ago

this is the fp16 one yolov4-tiny-fp16.zip

hhk7734 commented 4 years ago

For quantization, the process of converting to int takes a long time because it repeats the inference as many as num_calibration_steps.


This may be due to computer performance or memory capacity. I have no way of such errors.

Eric112752 commented 4 years ago

Thanks a lot, anyway, can you test the tiny-fp16 model inference at the iou_threshold of 0.6 or 0.7 then try to detect a bottle? Mine get crashed :/

hhk7734 commented 4 years ago

When the value rises to a certain level, it seems that it cannot escape the loop.