From a055821b732f748524680d08ffbcef5cbef25713 Mon Sep 17 00:00:00 2001
From: Edmond Yoo <hj3yoo@uwaterloo.ca>
Date: Mon, 17 Sep 2018 06:43:16 +0000
Subject: [PATCH] Fine-tuning opencv

---
 darknet.py |  406 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 381 insertions(+), 25 deletions(-)

diff --git a/darknet.py b/darknet.py
index 7d7dc69..f00356d 100644
--- a/darknet.py
+++ b/darknet.py
@@ -1,6 +1,38 @@
+#!python3
+"""
+Python 3 wrapper for identifying objects in images
+
+Requires DLL compilation
+
+Both the GPU and no-GPU version should be compiled; the no-GPU version should be renamed "yolo_cpp_dll_nogpu.dll".
+
+On a GPU system, you can force CPU evaluation by any of:
+
+- Set global variable DARKNET_FORCE_CPU to True
+- Set environment variable CUDA_VISIBLE_DEVICES to -1
+- Set environment variable "FORCE_CPU" to "true"
+
+
+To use, either run performDetect() after import, or modify the end of this file.
+
+See the docstring of performDetect() for parameters.
+
+Directly viewing or returning bounding-boxed images requires scikit-image to be installed (`pip install scikit-image`)
+
+
+Original *nix 2.7: https://github.com/pjreddie/darknet/blob/0f110834f4e18b30d5f101bf8f1724c34b7b83db/python/darknet.py
+Windows Python 2.7 version: https://github.com/AlexeyAB/darknet/blob/fc496d52bf22a0bb257300d3c79be9cd80e722cb/build/darknet/x64/darknet.py
+
+@author: Philip Kahn
+@date: 20180503
+"""
+#pylint: disable=R, W0401, W0614, W0703
 from ctypes import *
 import math
 import random
+import os
+import cv2
+import numpy as np
 
 def sample(probs):
     s = sum(probs)
@@ -42,11 +74,54 @@
     _fields_ = [("classes", c_int),
                 ("names", POINTER(c_char_p))]
 
-    
+
 
 #lib = CDLL("/home/pjreddie/documents/darknet/libdarknet.so", RTLD_GLOBAL)
-lib = CDLL("darknet.so", RTLD_GLOBAL)
-#lib = CDLL("yolo_cpp_dll.dll", RTLD_GLOBAL)
+#lib = CDLL("darknet.so", RTLD_GLOBAL)
+hasGPU = True
+if os.name == "nt":
+    cwd = os.path.dirname(__file__)
+    os.environ['PATH'] = cwd + ';' + os.environ['PATH']
+    winGPUdll = os.path.join(cwd, "yolo_cpp_dll.dll")
+    winNoGPUdll = os.path.join(cwd, "yolo_cpp_dll_nogpu.dll")
+    envKeys = list()
+    for k, v in os.environ.items():
+        envKeys.append(k)
+    try:
+        try:
+            tmp = os.environ["FORCE_CPU"].lower()
+            if tmp in ["1", "true", "yes", "on"]:
+                raise ValueError("ForceCPU")
+            else:
+                print("Flag value '"+tmp+"' not forcing CPU mode")
+        except KeyError:
+            # We never set the flag
+            if 'CUDA_VISIBLE_DEVICES' in envKeys:
+                if int(os.environ['CUDA_VISIBLE_DEVICES']) < 0:
+                    raise ValueError("ForceCPU")
+            try:
+                global DARKNET_FORCE_CPU
+                if DARKNET_FORCE_CPU:
+                    raise ValueError("ForceCPU")
+            except NameError:
+                pass
+            # print(os.environ.keys())
+            # print("FORCE_CPU flag undefined, proceeding with GPU")
+        if not os.path.exists(winGPUdll):
+            raise ValueError("NoDLL")
+        lib = CDLL(winGPUdll, RTLD_GLOBAL)
+    except (KeyError, ValueError):
+        hasGPU = False
+        if os.path.exists(winNoGPUdll):
+            lib = CDLL(winNoGPUdll, RTLD_GLOBAL)
+            print("Notice: CPU-only mode")
+        else:
+            # Try the other way, in case no_gpu was
+            # compile but not renamed
+            lib = CDLL(winGPUdll, RTLD_GLOBAL)
+            print("Environment variables indicated a CPU run, but we didn't find `"+winNoGPUdll+"`. Trying a GPU run anyway.")
+else:
+    lib = CDLL("./darknet.so", RTLD_GLOBAL)
 lib.network_width.argtypes = [c_void_p]
 lib.network_width.restype = c_int
 lib.network_height.argtypes = [c_void_p]
@@ -56,8 +131,9 @@
 predict.argtypes = [c_void_p, POINTER(c_float)]
 predict.restype = POINTER(c_float)
 
-set_gpu = lib.cuda_set_device
-set_gpu.argtypes = [c_int]
+if hasGPU:
+    set_gpu = lib.cuda_set_device
+    set_gpu.argtypes = [c_int]
 
 make_image = lib.make_image
 make_image.argtypes = [c_int, c_int, c_int]
@@ -87,6 +163,10 @@
 load_net.argtypes = [c_char_p, c_char_p, c_int]
 load_net.restype = c_void_p
 
+load_net_custom = lib.load_network_custom
+load_net_custom.argtypes = [c_char_p, c_char_p, c_int, c_int]
+load_net_custom.restype = c_void_p
+
 do_nms_obj = lib.do_nms_obj
 do_nms_obj.argtypes = [POINTER(DETECTION), c_int, c_int, c_float]
 
@@ -115,44 +195,320 @@
 predict_image.argtypes = [c_void_p, IMAGE]
 predict_image.restype = POINTER(c_float)
 
+def array_to_image(arr):
+    import numpy as np
+    # need to return old values to avoid python freeing memory
+    arr = arr.transpose(2,0,1)
+    c = arr.shape[0]
+    h = arr.shape[1]
+    w = arr.shape[2]
+    arr = np.ascontiguousarray(arr.flat, dtype=np.float32) / 255.0
+    data = arr.ctypes.data_as(POINTER(c_float))
+    im = IMAGE(w,h,c,data)
+    return im, arr
+
 def classify(net, meta, im):
     out = predict_image(net, im)
     res = []
     for i in range(meta.classes):
-        res.append((meta.names[i], out[i]))
+        if altNames is None:
+            nameTag = meta.names[i]
+        else:
+            nameTag = altNames[i]
+        res.append((nameTag, out[i]))
     res = sorted(res, key=lambda x: -x[1])
     return res
 
-def detect(net, meta, image, thresh=.5, hier_thresh=.5, nms=.45):
-    im = load_image(image, 0, 0)
+def detect(net, meta, image, thresh=.5, hier_thresh=.5, nms=.45, debug= False):
+    """
+    Performs the meat of the detection
+    """
+    #pylint: disable= C0321
+    if isinstance(image, np.ndarray):
+        im = array_to_image(image)[0]
+    else:
+        im = load_image(image, 0, 0)
+    #import cv2
+    #custom_image_bgr = cv2.imread(image) # use: detect(,,imagePath,)
+    #custom_image = cv2.cvtColor(custom_image_bgr, cv2.COLOR_BGR2RGB)
+    #custom_image = cv2.resize(custom_image,(lib.network_width(net), lib.network_height(net)), interpolation = cv2.INTER_LINEAR)
+    #import scipy.misc
+    #custom_image = scipy.misc.imread(image)
+    #im, arr = array_to_image(custom_image)		# you should comment line below: free_image(im)
+    if debug: print("Loaded image")
     num = c_int(0)
+    if debug: print("Assigned num")
     pnum = pointer(num)
+    if debug: print("Assigned pnum")
     predict_image(net, im)
-    dets = get_network_boxes(net, im.w, im.h, thresh, hier_thresh, None, 0, pnum, 1)
+    if debug: print("did prediction")
+    #dets = get_network_boxes(net, custom_image_bgr.shape[1], custom_image_bgr.shape[0], thresh, hier_thresh, None, 0, pnum, 0) # OpenCV
+    dets = get_network_boxes(net, im.w, im.h, thresh, hier_thresh, None, 0, pnum, 0)
+    if debug: print("Got dets")
     num = pnum[0]
-    #if (nms): do_nms_obj(dets, num, meta.classes, nms);
-    if (nms): do_nms_sort(dets, num, meta.classes, nms);
-    
+    if debug: print("got zeroth index of pnum")
+    if nms:
+        do_nms_sort(dets, num, meta.classes, nms)
+    if debug: print("did sort")
     res = []
+    if debug: print("about to range")
     for j in range(num):
+        if debug: print("Ranging on "+str(j)+" of "+str(num))
+        if debug: print("Classes: "+str(meta), meta.classes, meta.names)
         for i in range(meta.classes):
+            if debug: print("Class-ranging on "+str(i)+" of "+str(meta.classes)+"= "+str(dets[j].prob[i]))
             if dets[j].prob[i] > 0:
                 b = dets[j].bbox
-                res.append((meta.names[i], dets[j].prob[i], (b.x, b.y, b.w, b.h)))
+                if altNames is None:
+                    nameTag = meta.names[i]
+                else:
+                    nameTag = altNames[i]
+                if debug:
+                    print("Got bbox", b)
+                    print(nameTag)
+                    print(dets[j].prob[i])
+                    print((b.x, b.y, b.w, b.h))
+                res.append((nameTag, dets[j].prob[i], (b.x, b.y, b.w, b.h)))
+    if debug: print("did range")
     res = sorted(res, key=lambda x: -x[1])
+    if debug: print("did sort")
     free_image(im)
+    if debug: print("freed image")
     free_detections(dets, num)
+    if debug: print("freed detections")
     return res
-    
-if __name__ == "__main__":
-    #net = load_net("cfg/densenet201.cfg", "/home/pjreddie/trained/densenet201.weights", 0)
-    #im = load_image("data/wolf.jpg", 0, 0)
-    #meta = load_meta("cfg/imagenet1k.data")
-    #r = classify(net, meta, im)
-    #print r[:10]
-    net = load_net("cfg/yolov3.cfg", "yolov3.weights", 0)
-    meta = load_meta("data/coco.data")
-    r = detect(net, meta, "data/dog.jpg", 0.25)
-    print r
-    
 
+
+netMain = None
+metaMain = None
+altNames = None
+
+def performDetect(imagePath="data/dog.jpg", thresh= 0.25, configPath = "./cfg/yolov3.cfg", weightPath = "yolov3.weights", metaPath= "./data/coco.data", showImage= True, makeImageOnly = False, initOnly= False):
+    """
+    Convenience function to handle the detection and returns of objects.
+
+    Displaying bounding boxes requires libraries scikit-image and numpy
+
+    Parameters
+    ----------------
+    imagePath: str
+        Path to the image to evaluate. Raises ValueError if not found
+
+    thresh: float (default= 0.25)
+        The detection threshold
+
+    configPath: str
+        Path to the configuration file. Raises ValueError if not found
+
+    weightPath: str
+        Path to the weights file. Raises ValueError if not found
+
+    metaPath: str
+        Path to the data file. Raises ValueError if not found
+
+    showImage: bool (default= True)
+        Compute (and show) bounding boxes. Changes return.
+
+    makeImageOnly: bool (default= False)
+        If showImage is True, this won't actually *show* the image, but will create the array and return it.
+
+    initOnly: bool (default= False)
+        Only initialize globals. Don't actually run a prediction.
+
+    Returns
+    ----------------------
+
+
+    When showImage is False, list of tuples like
+        ('obj_label', confidence, (bounding_box_x_px, bounding_box_y_px, bounding_box_width_px, bounding_box_height_px))
+        The X and Y coordinates are from the center of the bounding box. Subtract half the width or height to get the lower corner.
+
+    Otherwise, a dict with
+        {
+            "detections": as above
+            "image": a numpy array representing an image, compatible with scikit-image
+            "caption": an image caption
+        }
+    """
+    # Import the global variables. This lets us instance Darknet once, then just call performDetect() again without instancing again
+    global metaMain, netMain, altNames #pylint: disable=W0603
+    assert 0 < thresh < 1, "Threshold should be a float between zero and one (non-inclusive)"
+    if not os.path.exists(configPath):
+        raise ValueError("Invalid config path `"+os.path.abspath(configPath)+"`")
+    if not os.path.exists(weightPath):
+        raise ValueError("Invalid weight path `"+os.path.abspath(weightPath)+"`")
+    if not os.path.exists(metaPath):
+        raise ValueError("Invalid data file path `"+os.path.abspath(metaPath)+"`")
+    if netMain is None:
+        netMain = load_net_custom(configPath.encode("ascii"), weightPath.encode("ascii"), 0, 1)  # batch size = 1
+    if metaMain is None:
+        metaMain = load_meta(metaPath.encode("ascii"))
+    if altNames is None:
+        # In Python 3, the metafile default access craps out on Windows (but not Linux)
+        # Read the names file and create a list to feed to detect
+        try:
+            with open(metaPath) as metaFH:
+                metaContents = metaFH.read()
+                import re
+                match = re.search("names *= *(.*)$", metaContents, re.IGNORECASE | re.MULTILINE)
+                if match:
+                    result = match.group(1)
+                else:
+                    result = None
+                try:
+                    if os.path.exists(result):
+                        with open(result) as namesFH:
+                            namesList = namesFH.read().strip().split("\n")
+                            altNames = [x.strip() for x in namesList]
+                except TypeError:
+                    pass
+        except Exception:
+            pass
+    if initOnly:
+        print("Initialized detector")
+        return None
+    if not os.path.exists(imagePath):
+        raise ValueError("Invalid image path `"+os.path.abspath(imagePath)+"`")
+    # Do the detection
+    #detections = detect(netMain, metaMain, imagePath, thresh)	# if is used cv2.imread(image)
+    #detections = detect(netMain, metaMain, imagePath.encode("ascii"), thresh)
+    detections = detect(netMain, metaMain, cv2.imread(imagePath), thresh, debug=True)
+    if showImage:
+        try:
+            from skimage import io, draw
+            import numpy as np
+            image = io.imread(imagePath)
+            print("*** "+str(len(detections))+" Results, color coded by confidence ***")
+            imcaption = []
+            for detection in detections:
+                label = detection[0]
+                confidence = detection[1]
+                pstring = label+": "+str(np.rint(100 * confidence))+"%"
+                imcaption.append(pstring)
+                print(pstring)
+                bounds = detection[2]
+                shape = image.shape
+                # x = shape[1]
+                # xExtent = int(x * bounds[2] / 100)
+                # y = shape[0]
+                # yExtent = int(y * bounds[3] / 100)
+                yExtent = int(bounds[3])
+                xEntent = int(bounds[2])
+                # Coordinates are around the center
+                xCoord = int(bounds[0] - bounds[2]/2)
+                yCoord = int(bounds[1] - bounds[3]/2)
+                boundingBox = [
+                    [xCoord, yCoord],
+                    [xCoord, yCoord + yExtent],
+                    [xCoord + xEntent, yCoord + yExtent],
+                    [xCoord + xEntent, yCoord]
+                ]
+                # Wiggle it around to make a 3px border
+                rr, cc = draw.polygon_perimeter([x[1] for x in boundingBox], [x[0] for x in boundingBox], shape= shape)
+                rr2, cc2 = draw.polygon_perimeter([x[1] + 1 for x in boundingBox], [x[0] for x in boundingBox], shape= shape)
+                rr3, cc3 = draw.polygon_perimeter([x[1] - 1 for x in boundingBox], [x[0] for x in boundingBox], shape= shape)
+                rr4, cc4 = draw.polygon_perimeter([x[1] for x in boundingBox], [x[0] + 1 for x in boundingBox], shape= shape)
+                rr5, cc5 = draw.polygon_perimeter([x[1] for x in boundingBox], [x[0] - 1 for x in boundingBox], shape= shape)
+                boxColor = (int(255 * (1 - (confidence ** 2))), int(255 * (confidence ** 2)), 0)
+                draw.set_color(image, (rr, cc), boxColor, alpha= 0.8)
+                draw.set_color(image, (rr2, cc2), boxColor, alpha= 0.8)
+                draw.set_color(image, (rr3, cc3), boxColor, alpha= 0.8)
+                draw.set_color(image, (rr4, cc4), boxColor, alpha= 0.8)
+                draw.set_color(image, (rr5, cc5), boxColor, alpha= 0.8)
+            if not makeImageOnly:
+                io.imshow(image)
+                io.show()
+            detections = {
+                "detections": detections,
+                "image": image,
+                "caption": "\n<br/>".join(imcaption)
+            }
+        except Exception as e:
+            print("Unable to show image: "+str(e))
+    return detections
+
+
+def capture(thresh=.25, hier_thresh=.5, nms=.45, configPath="./cfg/yolov3.cfg", weightPath="yolov3.weights",
+            metaPath="./data/coco.data", showImage=True, makeImageOnly=False, initOnly=False):
+    global metaMain, netMain, altNames  # pylint: disable=W0603
+    netMain = load_net_custom(configPath.encode("ascii"), weightPath.encode("ascii"), 0, 1)  # batch size = 1
+    metaMain = load_meta(metaPath.encode("ascii"))
+
+    num = c_int(0)
+    pnum = pointer(num)
+    num = pnum[0]
+
+    capture = cv2.VideoCapture('../data/test1.mp4')
+    print(capture.get(cv2.CAP_PROP_FPS))
+
+    capture.set(cv2.CAP_PROP_FRAME_WIDTH, 1024)
+    capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 768)
+
+    while True:
+        ret, frame = capture.read()
+        detections = detect(netMain, metaMain, frame, thresh, debug=True)
+        '''
+        im, arr = array_to_image(frame)
+        predict_image(netMain, im)
+        dets = get_network_boxes(netMain, im.w, im.h, thresh, hier_thresh, None, 0, pnum, 1)
+        if nms:
+            do_nms_sort(dets, num, metaMain.classes, nms)
+        res = []
+        for j in range(num):
+            for i in range(metaMain.classes):
+                if dets[j].prob[i] > 0:
+                    b = dets[j].bbox
+                    nameTag = metaMain.names[i]
+                    res.append((nameTag, dets[j].prob[i], (b.x, b.y, b.w, b.h)))
+        '''
+        for detection in detections:
+            label = detection[0]
+            confidence = detection[1]
+            pstring = label + ": " + str(np.rint(100 * confidence)) + "%"
+            imcaption.append(pstring)
+            print(pstring)
+            bounds = detection[2]
+            shape = image.shape
+            # x = shape[1]
+            # xExtent = int(x * bounds[2] / 100)
+            # y = shape[0]
+            # yExtent = int(y * bounds[3] / 100)
+            yExtent = int(bounds[3])
+            xEntent = int(bounds[2])
+            # Coordinates are around the center
+            xCoord = int(bounds[0] - bounds[2] / 2)
+            yCoord = int(bounds[1] - bounds[3] / 2)
+            boundingBox = [
+                [xCoord, yCoord],
+                [xCoord, yCoord + yExtent],
+                [xCoord + xEntent, yCoord + yExtent],
+                [xCoord + xEntent, yCoord]
+            ]
+            # Wiggle it around to make a 3px border
+            rr, cc = draw.polygon_perimeter([x[1] for x in boundingBox], [x[0] for x in boundingBox], shape=shape)
+            rr2, cc2 = draw.polygon_perimeter([x[1] + 1 for x in boundingBox], [x[0] for x in boundingBox], shape=shape)
+            rr3, cc3 = draw.polygon_perimeter([x[1] - 1 for x in boundingBox], [x[0] for x in boundingBox], shape=shape)
+            rr4, cc4 = draw.polygon_perimeter([x[1] for x in boundingBox], [x[0] + 1 for x in boundingBox], shape=shape)
+            rr5, cc5 = draw.polygon_perimeter([x[1] for x in boundingBox], [x[0] - 1 for x in boundingBox], shape=shape)
+            boxColor = (int(255 * (1 - (confidence ** 2))), int(255 * (confidence ** 2)), 0)
+            draw.set_color(image, (rr, cc), boxColor, alpha=0.8)
+            draw.set_color(image, (rr2, cc2), boxColor, alpha=0.8)
+            draw.set_color(image, (rr3, cc3), boxColor, alpha=0.8)
+            draw.set_color(image, (rr4, cc4), boxColor, alpha=0.8)
+            draw.set_color(image, (rr5, cc5), boxColor, alpha=0.8)
+        print(res)
+        cv2.imshow('frame', frame)
+        if cv2.waitKey(1) & 0xFF == ord('q'):
+            break
+
+    capture.release()
+    cv2.destroyAllWindows()
+
+
+if __name__ == "__main__":
+    performDetect(imagePath='data/scream.jpg')
+    #performDetect(imagePath="../data/test1.jpg", thresh=0.25, configPath="./cfg/tiny_yolo.cfg",
+    #              weightPath="./weights/second_general/tiny_yolo_17000.weights",
+    #              metaPath="./data/obj.data", showImage=True, makeImageOnly=False, initOnly=False)
+    #print(performDetect(showImage=False))
+    #capture()

--
Gitblit v1.10.0