3

I am using this git package to run object detection in python using YOLOv4

https://github.com/erentknn/yolov4-object-detection

The script works great and I can print the objects found with confidence in the terminal, but when I execute it from PHP I get back empty results. Im guessing maybe because the PHP script is waiting for the python to finish and not returning the results in real time. I tried creating a dictionary to store the results and return at the end but still returns nothing. I was able to do it in YOLOv3 easily, not sure what changed with v4.

Edit: After some more test I cant even write the results to a file which is strange. I can if run from the terminal.

Edit: If I var_dump($output) it returns NULL. With debugging on I don't get any extra info returned.

Im running the script - yolo_video.py

# example usage: python3 yolo_video.py -i video.mp4 -o video_out.avi
import argparse
import glob
import time
import logging
from pathlib import Path
import cv2
import numpy as np

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
formatter = logging.Formatter("%(asctime)s-%(name)s-%(message)s")

stream_handler = logging.StreamHandler()
stream_handler.setFormatter(formatter)
logger.addHandler(stream_handler)

parser = argparse.ArgumentParser()
parser.add_argument("-i", "--input", type=str, default="",
                    help="video.mp4")
parser.add_argument("-o", "--output", type=str, default="",
                    help="path to (optional) output video file")
parser.add_argument("-d", "--display", type=int, default=1,
                    help="display output or not (1/0)")
parser.add_argument("-ht", "--height", type=int, default=1200,
                    help="height of output")
parser.add_argument("-wt", "--width", type=int, default=700,
                    help="width of output")
parser.add_argument("-c", "--confidence", type=float, default=0.8,
                    help="confidence threshold")
parser.add_argument("-t", "--threshold", type=float, default=0.6,
                    help="non-maximum supression threshold")

args = parser.parse_args()
logger.info("Parsed Arguments")

CONFIDENCE_THRESHOLD = args.confidence
NMS_THRESHOLD = args.threshold
if not Path(args.input).exists():
    raise FileNotFoundError("Path to video file is not exist.")

vc = cv2.VideoCapture(args.input)
weights = glob.glob("yolo/*.weights")[0]
labels = glob.glob("yolo/*.txt")[0]
cfg = glob.glob("yolo/*.cfg")[0]

logger.info("Using {} weights ,{} configs and {} labels.".format(weights, cfg, labels))

class_names = list()
with open(labels, "r") as f:
    class_names = [cname.strip() for cname in f.readlines()]

COLORS = np.random.randint(0, 255, size=(len(class_names), 3), dtype="uint8")

net = cv2.dnn.readNetFromDarknet(cfg, weights)
net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)
net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA)

layer = net.getLayerNames()
layer = [layer[i[0] - 1] for i in net.getUnconnectedOutLayers()]
writer = None


def detect(frm, net, ln):
    (H, W) = frm.shape[:2]
    blob = cv2.dnn.blobFromImage(frm, 1 / 255.0, (416, 416), swapRB=True, crop=False)
    net.setInput(blob)
    start_time = time.time()
    layerOutputs = net.forward(ln)
    end_time = time.time()

    boxes = []
    classIds = []
    confidences = []
    for output in layerOutputs:
        for detection in output:
            scores = detection[5:]
            classID = np.argmax(scores)
            confidence = scores[classID]

            if confidence > CONFIDENCE_THRESHOLD:
                box = detection[0:4] * np.array([W, H, W, H])
                (centerX, centerY, width, height) = box.astype("int")
                x = int(centerX - (width / 2))
                y = int(centerY - (height / 2))

                boxes.append([x, y, int(width), int(height)])
                classIds.append(classID)
                confidences.append(float(confidence))
                
    idxs = cv2.dnn.NMSBoxes(boxes, confidences, CONFIDENCE_THRESHOLD, NMS_THRESHOLD)

    if len(idxs) > 0:
        for i in idxs.flatten():
            (x, y) = (boxes[i][0], boxes[i][1])
            (w, h) = (boxes[i][2], boxes[i][3])

            color = [int(c) for c in COLORS[classIds[i]]]
            cv2.rectangle(frm, (x, y), (x + w, y + h), color, 2)
            text = "{}: {:.4f}".format(class_names[classIds[i]], confidences[i])
            
            # HERE IM PRINTING THE RESULTS (WORKS IN TERMNAL)
            print("found")
            print(confidences[i])
            print(class_names[classIds[i]])

            cv2.putText(
                frm, text, (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2
            )
            
            fps_label = "FPS: %.2f" % (1 / (end_time - start_time))
            cv2.putText(
                frm, fps_label, (0, 25), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2
            )


while cv2.waitKey(1) < 1:
    (grabbed, frame) = vc.read()
    if not grabbed:
        break
    frame = cv2.resize(frame, (args.height, args.width))
    detect(frame, net, layer)

    if writer is not None:
        writer.write(frame)

Then in my PHP script

$command = escapeshellcmd('python3 yolo_video.py -i video.mp4 -o video_out.avi');
$output = shell_exec($command);
echo $output;

How can I get the results from the python script to echo out in PHP? Im not getting any errors and the script is completing.

Ryan D
  • 741
  • 1
  • 11
  • 29
  • If you do `var_dump($output);` does it give a string? false? or null? – Gerard van den Bosch Sep 05 '22 at 03:43
  • @GerardvandenBoschit it returns NULL – Ryan D Sep 06 '22 at 04:04
  • @RyanD Can you edit the post with the logger output? also if there isn't anything useful there, might consider changing the logging level to `Debug` https://docs.python.org/3/library/logging.html#levels – Warkaz Sep 06 '22 at 09:14
  • @Warkaz I have debug on and it returns empty, thats what is making this so difficult. No errors or helpful info being returned other than NULL if I var_dump the output. – Ryan D Sep 06 '22 at 22:11
  • @RyanD please check the answer, in addition to already stated, only think that there is a problem with way of execution from php, but already checked that indeed is the proper way. Is the system Linux, maybe some permission issues for execution? – Warkaz Sep 06 '22 at 22:44

2 Answers2

2

cv2.waitKey is not working well in general, PHP or jupyter notebook depending on your machine.

I tried this on my machine and it solved the problem :

while(vc.isOpened()):

if frame is None:
        break

or

if not grabbed:
        break

The script will stop when the video is finished.

L4ur3nt
  • 98
  • 6
0

After rechecking your code, I believe the main reason is waitKey(1):

while cv2.waitKey(1) < 1:
    (grabbed, frame) = vc.read()
    if not grabbed:
        break
    frame = cv2.resize(frame, (args.height, args.width))
    detect(frame, net, layer)

    if writer is not None:
        writer.write(frame)

Not sure how it is suppose to wait for interaction from the user as it is called from a another script. But cv.waitKey(1) is suppose to return the key pressed after 1 ms (technically more) of waiting from the user. So your condition is probably while None < 1. Check more on a similar issue, but in essence the read returns (return value, image) where return value is was it successful.

Another usual culprit in this kind of scenarios is argparse but would check first maybe to reformulate to loop over frames of the video itself in the form of checking the output of read. This can be worked around with a config file.

Try replacing the condition to something like:

while True:

Checked around, and indeed there is a better condition while(vc.isOpened()):. Would suggest you read the question that explains it.

Warkaz
  • 845
  • 6
  • 18
  • Thank you for this great explanation, It makes sense what you're saying im just not sure how I can go about fixing it. Ill read up on the resource you provided. – Ryan D Sep 07 '22 at 06:11
  • Thank you for the info, i'm not sure how I can fix my issue though or if its just the way it is. – Ryan D Sep 07 '22 at 06:12
  • @RyanD Added example of an possible edit which could change this behavior. – Warkaz Sep 07 '22 at 09:33
  • Thank you for the example, I get NameError: name 'true' is not defined int(1) – Ryan D Sep 07 '22 at 17:28
  • yeah True should be capitalized @RyanD – Warkaz Sep 07 '22 at 18:22
  • Ok yeah I should of known better thank you, but still same result. I can get a printed value from that while True loop but not from the if len(idxs) > 0: loop – Ryan D Sep 07 '22 at 23:06
  • @RyanD Maybe you are getting inside without an actual frame retrieved, would suggest to look up the second link (https://stackoverflow.com/a/44104194/10985412) about how they did the while loop, also edited the question to include it – Warkaz Sep 08 '22 at 07:50
  • Thanks yeah they are using yolo v3 which I can also get to work just fine. Its the Yolo V4 that doesn't want to play nicely. – Ryan D Sep 08 '22 at 20:03