From 8cd1ea0ae226578eb123b15a35b1d5afacfbd4d3 Mon Sep 17 00:00:00 2001 From: rish-16 Date: Mon, 27 Jan 2020 23:12:19 +0800 Subject: [PATCH] Uploaded to PyPi --- __pycache__/blocks.cpython-37.pyc | Bin 4014 -> 0 bytes __pycache__/sight.cpython-37.pyc | Bin 3450 -> 0 bytes __pycache__/zoo.cpython-37.pyc | Bin 10866 -> 0 bytes build/lib/sightseer/__init__.py | 6 + {sight => build/lib/sightseer}/blocks.py | 0 {sight => build/lib/sightseer}/proc.py | 0 .../lib/sightseer/sightseer.py | 0 {sight => build/lib/sightseer}/zoo.py | 0 dist/sightseer-1.0.0-py3-none-any.whl | Bin 0 -> 14901 bytes dist/sightseer-1.0.0.tar.gz | Bin 0 -> 10779 bytes dist/sightseer-1.0.1-py3-none-any.whl | Bin 0 -> 14902 bytes dist/sightseer-1.0.1.tar.gz | Bin 0 -> 10787 bytes main.py | 4 +- setup.py | 18 + sightseer.egg-info/PKG-INFO | 116 ++++++ sightseer.egg-info/SOURCES.txt | 12 + sightseer.egg-info/dependency_links.txt | 1 + sightseer.egg-info/not-zip-safe | 1 + sightseer.egg-info/top_level.txt | 1 + sightseer/__init__.py | 6 + sightseer/blocks.py | 123 ++++++ sightseer/proc.py | 106 +++++ sightseer/sightseer.py | 125 ++++++ sightseer/zoo.py | 369 ++++++++++++++++++ 24 files changed, 886 insertions(+), 2 deletions(-) delete mode 100644 __pycache__/blocks.cpython-37.pyc delete mode 100644 __pycache__/sight.cpython-37.pyc delete mode 100644 __pycache__/zoo.cpython-37.pyc create mode 100644 build/lib/sightseer/__init__.py rename {sight => build/lib/sightseer}/blocks.py (100%) rename {sight => build/lib/sightseer}/proc.py (100%) rename sight/sight.py => build/lib/sightseer/sightseer.py (100%) rename {sight => build/lib/sightseer}/zoo.py (100%) create mode 100644 dist/sightseer-1.0.0-py3-none-any.whl create mode 100644 dist/sightseer-1.0.0.tar.gz create mode 100644 dist/sightseer-1.0.1-py3-none-any.whl create mode 100644 dist/sightseer-1.0.1.tar.gz create mode 100644 setup.py create mode 100644 sightseer.egg-info/PKG-INFO create mode 100644 sightseer.egg-info/SOURCES.txt create mode 100644 sightseer.egg-info/dependency_links.txt create mode 100644 sightseer.egg-info/not-zip-safe create mode 100644 sightseer.egg-info/top_level.txt create mode 100644 sightseer/__init__.py create mode 100644 sightseer/blocks.py create mode 100644 sightseer/proc.py create mode 100644 sightseer/sightseer.py create mode 100644 sightseer/zoo.py diff --git a/__pycache__/blocks.cpython-37.pyc b/__pycache__/blocks.cpython-37.pyc deleted file mode 100644 index 969367d8113772b8e01ed2a0e5b52b4a930e47f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4014 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 PIbavR8+>7T{=NSJ0NK*c diff --git a/__pycache__/sight.cpython-37.pyc b/__pycache__/sight.cpython-37.pyc deleted file mode 100644 index 8d6199217568d84aafa4845fe89f2b518ff79286..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3450 zcmaJ^OK;rB5oYr>9M0&ueyp`#XLhqmb^>RF7y%L>!NRs|%Z9zOAS?E|5rm=Gtq~;- zIc`#tC2)Wsc8&>h&LJ2bbIT!zJ>+lX!c%~p^cQ^DeAOI1qIlB;i`CuLJyl&_RoC2| zn(_^_e`Ys-9k&eQ-_$v2HkjLZ^WT67L$Ji)^_Ou|_bqPYYbMUXa5AA2-AKdBU*XV8eneY&DTMs!I?0iEfeNjgImH9Homs_ zP&9@AmT^a16H{UuoGTW@jF<)25OZQ4Ql9ugT*9|0J`$J370meJDm1-bHJ`=(ts<9F zMJ9fQ8hzWK?c|j1T zaS;ThN3Jzwhx4a8_fDoymA4>A-qy;`gDH$LD_LP|n@8rzEX~>otFofKTa_2>6-!x? zDL%dQ^vTn;;O_F;t>v{lFT0lVu()zIRXC8`@55_>=CzSEA)TsOB~u|)KoWTrCUOd+ zIjLZE%-Y+WPxBuY~+^i*n}lkX&;%)nDp$@ zpl-iF>a9r zqhD-eo(cQ5QB0NA8@6Mro2$mFn=j~Br;iLRGeh5!slOGo?&DVxh(jlr8}#^>e|!Q;KACRFYgC09^G4i$S+?Q zeR%)T^27DYRx%WoGgNU}R1R=9lxhYWb9*z1hm{)-@~v#Ya>L;e-l-bR!$wGevN>Zl29@YL|BBC2Tir7D{m)Gl3)jW zI{UFGwkmf^5_Gv0g-}34CFd3vYwItfrlk|ZzTAky!5kFNzYk)VJ|Ht~+NR0c%r|Yk zZOvU_uIVw4M%dxyGf17=<(;4H7Q@}57w*N!f5Ndx1Yl|eND5FL68``L2mw%kG)5jA zZI({y2~$`{_NWOE`LFj1Yit+JwkvF5mu}hEw1x8r0$>aBjS_U*19;ki)$_7BZWca) z20#!1S*vV8vaKQHmTe6ze>?@Q2G+CyP=p6y&6KtVRtLc909eiO>;zbI8d#S4nlve~ z`^Yc-39y`uyu+n-~2XIo!ceSi40p2m`=0OU(kVraV;RLvXNcd zV@qcpXXNTU#7jr*ogXa#q-|%CiyiMs;qX{KSL>nD~?Ya|1BB z0hrk1)&wSP4HKsRTuhCpFXSJ4<)Zs%bm~!;p}W4X)K%g>Byx=i<&XdGz%yiYz1o%9 zbL3C$KJMov-Cm<&ng+u0RY%8PR4gJG^uu`^8j8{nz#HoY=ksretH3dw?43 z7UngBN&t*l7y&2jYbfBZ;(`?JHRCE~1*39-O2OYl8W3bOiy3`x-Nwp0ws`yvTE%)g z+Z_6VkOk@(DSC>M4T>TsA0}~8+1pv1PAUoRYefSpo47~Cpbm4Yn9daw^L^Yen{gT@ zpmmLvKq9A0(?A@1crUR9^YHrFDg5t}$Rcqx3RvPb5^jRgIKXM>5Hof7nCf$cBf@uN zVb(5jGF0N$Ws*}_!=%tr!EL%y)labtZ{*>g+>HD1bzLQC+bW-iQSHJZyq(JZQyVV2 zxIHO_wCFB&oH!W(UqjUX!jh8Sk?7 zZi^~OI`JT={2&-)VmBea9R#m-!=#>3_}eha1D_K|DO4>I`7sePUHzQMnb$N&6`P`n zqh3zx;jXqk+y5BPg4ghxdbT@PM~0K`4u%IhnQ*_5X`ZRgB-`g^vB^I>>-Tn~3iIAj z$)U<3ndhjPd-}u_MYtlza%=U`llq#P8-&F$$%-W2K;cXrck8eE8A;BcpAQSfZ>}g( iDmUApGF+?pKFcca7xi&{K?x?OS`OynnMFwAG5-T!HZ7w7 diff --git a/__pycache__/zoo.cpython-37.pyc b/__pycache__/zoo.cpython-37.pyc deleted file mode 100644 index 65bed1c861add2e93ef6255dc723fc99e1912b78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10866 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 diff --git a/build/lib/sightseer/__init__.py b/build/lib/sightseer/__init__.py new file mode 100644 index 0000000..da1bc57 --- /dev/null +++ b/build/lib/sightseer/__init__.py @@ -0,0 +1,6 @@ +# __init__.py +__version__ = "1.0.1" + +from sightseer.sightseer import Sightseer +from sightseer.zoo import * +from sightseer.proc import * \ No newline at end of file diff --git a/sight/blocks.py b/build/lib/sightseer/blocks.py similarity index 100% rename from sight/blocks.py rename to build/lib/sightseer/blocks.py diff --git a/sight/proc.py b/build/lib/sightseer/proc.py similarity index 100% rename from sight/proc.py rename to build/lib/sightseer/proc.py diff --git a/sight/sight.py b/build/lib/sightseer/sightseer.py similarity index 100% rename from sight/sight.py rename to build/lib/sightseer/sightseer.py diff --git a/sight/zoo.py b/build/lib/sightseer/zoo.py similarity index 100% rename from sight/zoo.py rename to build/lib/sightseer/zoo.py diff --git a/dist/sightseer-1.0.0-py3-none-any.whl b/dist/sightseer-1.0.0-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..d73b25133dde373215c2c8099be5d24b23c6c619 GIT binary patch literal 14901 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 literal 0 HcmV?d00001 diff --git a/dist/sightseer-1.0.0.tar.gz b/dist/sightseer-1.0.0.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..cfb36a2d4b33cbb31dd8ea0c21d698fd9965a632 GIT binary patch literal 10779 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 RH@|=)ex7tnVB=t5{|j@+JVyWk literal 0 HcmV?d00001 diff --git a/dist/sightseer-1.0.1-py3-none-any.whl b/dist/sightseer-1.0.1-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..e82fb021279a77e7272352d78b0dd6a4b49606fa GIT binary patch literal 14902 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%0DLm!LrOw0O6L*C18@&C|O@`BB5zSoR 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 +
+ +
+

+ +

+ + AUR license + +

+ +

+

State-of-the-art Computer Vision and Object Detection for TensorFlow.

+

+ + *Sight* provides state-of-the-art general-purpose architectures (YOLO9000, MaskRCNN, Fast/Faster RCNN, SSD...) for Computer Vision and Object Detection tasks with 30+ pretrained models written in TensorFlow 1.15. + + ## Installation + + `sight` is written in Python 3.5+ and TensorFlow 1.15. + + Ideally, `sight` should be installed in a [virtual environments](https://docs.python.org/3/library/venv.html). If you're unfamiliar with Python virtual environments, check out this [tutorial](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/) on getting started. + + ### Via PyPi + + To use `sight`, you must first have TensorFlow installed. To do so, follow the instructions on the [TensorFlow installation page](https://www.tensorflow.org/install/pip?lang=python3). + + When your virtual environment is set up with TensorFlow, you can install `sight` using `pip`: + + ```bash + pip install sight + ``` + + ### From Source + + Again, to install from source, you need TensorFlow 1.15 and above running in a virtual environment. You can install the package by cloning the repo and installing the dependencies: + + ```bash + git clone https://github.com/rish-16/sight + cd sight + pip install . + ``` + + ### Model Architectures + + 1. YOLOv3 (Darknet by Joseph Redmon) + 2. Mask R-CNN (Facebook AI Research) + + ## Usage + + 1a. Loading images + + ```python + from sight import Sightseer + + ss = Sightseer() + image = ss.load_image("path/to/image") + ``` + + 1b. Loading videos + + ```python + from sight import Sightseer + + ss = Sightseer() + frames = ss.load_vidsource("path/to/video", return_data=True) + ``` + + 1c. Loading webcam footage + + ```python + from sight import Sightseer + + ss = Sightseer() + image = ss.load_webcam() + ``` + + 1d. Loading screen grab footage + + ```python + from sight import Sightseer + + ss = Sightseer() + image = ss.screen_grab() + ``` + + 2. Using models from `sight.zoo` + + Once installed, any model offered by `sight` can be accessed in less than 10 lines of code. For instance, the code to use the YOLOv3 (Darknet) model is as follows: + + ```python + from sight import Sightseer + from sight.zoo import YOLOv3Client + + yolo = YOLOv3Client() + yolo.load_model() # downloads weights + + # loading images from local system + ss = Sightseer("path/to/img") + image = ss.load_image() + + # returns array of labels, confidence, and bounding box info + preds, pred_img = yolo.predict(image, return_image=True) + ss.render_image(pred_img) + ``` +Platform: UNKNOWN +Description-Content-Type: text/markdown diff --git a/sightseer.egg-info/SOURCES.txt b/sightseer.egg-info/SOURCES.txt new file mode 100644 index 0000000..aba0333 --- /dev/null +++ b/sightseer.egg-info/SOURCES.txt @@ -0,0 +1,12 @@ +README.md +setup.py +sightseer/__init__.py +sightseer/blocks.py +sightseer/proc.py +sightseer/sightseer.py +sightseer/zoo.py +sightseer.egg-info/PKG-INFO +sightseer.egg-info/SOURCES.txt +sightseer.egg-info/dependency_links.txt +sightseer.egg-info/not-zip-safe +sightseer.egg-info/top_level.txt \ No newline at end of file diff --git a/sightseer.egg-info/dependency_links.txt b/sightseer.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/sightseer.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/sightseer.egg-info/not-zip-safe b/sightseer.egg-info/not-zip-safe new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/sightseer.egg-info/not-zip-safe @@ -0,0 +1 @@ + diff --git a/sightseer.egg-info/top_level.txt b/sightseer.egg-info/top_level.txt new file mode 100644 index 0000000..d251932 --- /dev/null +++ b/sightseer.egg-info/top_level.txt @@ -0,0 +1 @@ +sightseer diff --git a/sightseer/__init__.py b/sightseer/__init__.py new file mode 100644 index 0000000..da1bc57 --- /dev/null +++ b/sightseer/__init__.py @@ -0,0 +1,6 @@ +# __init__.py +__version__ = "1.0.1" + +from sightseer.sightseer import Sightseer +from sightseer.zoo import * +from sightseer.proc import * \ No newline at end of file diff --git a/sightseer/blocks.py b/sightseer/blocks.py new file mode 100644 index 0000000..4c91461 --- /dev/null +++ b/sightseer/blocks.py @@ -0,0 +1,123 @@ +import struct +import numpy as np +import tensorflow as tf +from tensorflow.keras.layers import Conv2D, ZeroPadding2D, BatchNormalization, LeakyReLU, add + +class BoundingBox(object): + def __init__(self, xmin, ymin, xmax, ymax, objectness=None, classes=None): + self.xmin = xmin + self.ymin = ymin + self.xmax = xmax + self.ymax = ymax + + self.objectness = objectness + self.classes = classes + + self.label = -1 + self.confidence = -1 + + def get_label(self): + if self.label == -1: + self.label = np.argmax(self.classes) + return self.label + + def get_confidence(self): + if self.confidence == -1: + self.confidence = self.classes[self.get_label()] + return self.confidence + +class SightLoader(): + def __init__(self, weights_path): + """ + Weights loading framework for all Sight models + """ + with open(weights_path, 'rb') as wf: + major, = struct.unpack('i', wf.read(4)) + minor, = struct.unpack('i', wf.read(4)) + revision, = struct.unpack('i', wf.read(4)) + + if (major*10+ minor) >= 2 and major < 1000 and minor < 1000: + wf.read(8) + else: + wf.read(4) + + transpose = (major > 1000) or (minor > 1000) + + binary = wf.read() + + self.offset = 0 + self.all_weights = np.frombuffer(binary, dtype="float32") + + def read_bytes(self, chunk_size): + self.offset = self.offset + chunk_size + return self.all_weights[self.offset - chunk_size:self.offset] + + def load_weights(self, model, verbose=True): + for i in range(106): # standard darknet layer count + try: + conv_layer = model.get_layer("conv_" + str(i)) + + if verbose: + print ("Loading Convolution #{}".format(i)) + + if i not in [81, 93, 105]: + norm_layer = model.get_layer("bnorm_" + str(i)) + + size = np.prod(norm_layer.get_weights()[0].shape) + + beta = self.read_bytes(size) + gamma = self.read_bytes(size) + mean = self.read_bytes(size) + var = self.read_bytes(size) + + weights = norm_layer.set_weights([gamma, beta, mean, var]) + + if len(conv_layer.get_weights()) > 1: + bias = self.read_bytes(np.prod(conv_layer.get_weights()[1].shape)) + kernel = self.read_bytes(np.prod(conv_layer.get_weights()[0].shape)) + + kernel = kernel.reshape(list(reversed(conv_layer.get_weights()[0].shape))) + kernel = kernel.transpose([2, 3, 1, 0]) + conv_layer.set_weights([kernel, bias]) + else: + kernel = self.read_bytes(np.prod(conv_layer.get_weights()[0].shape)) + kernel = kernel.reshape(list(reversed(conv_layer.get_weights()[0].shape))) + kernel = kernel.transpose([2, 3, 1, 0]) + conv_layer.set_weights([kernel]) + + except ValueError: + if verbose: + print ("No Convolution #{}".format(i)) + else: + pass + + if verbose: + print ("Finished loading weights into model. Predicting on input data...") + + def reset_offset(self): + self.offset = 0 + +class ConvBlock(): + def get_conv_block(inp, convs, skip=True): + x = inp + count = 0 + + for conv in convs: + if count == (len(convs) - 2) and skip: + skip_conn = x + count += 1 + + if conv['stride'] > 1: x = ZeroPadding2D(((1,0),(1,0)))(x) + + x = Conv2D(conv['filter'], + conv['kernel'], + strides=conv['stride'], + padding="valid" if conv['stride']>1 else "same", + name="conv_"+str(conv['layer_idx']), + use_bias=False if conv['bnorm'] else True)(x) + + if conv['bnorm']: x = BatchNormalization(epsilon=0.001, name="bnorm_"+str(conv['layer_idx']))(x) + + if conv['leaky']: x = LeakyReLU(alpha=0.1, name="leaky_"+str(conv['layer_idx']))(x) + + return add([skip_conn, x]) if skip else x \ No newline at end of file diff --git a/sightseer/proc.py b/sightseer/proc.py new file mode 100644 index 0000000..6a71697 --- /dev/null +++ b/sightseer/proc.py @@ -0,0 +1,106 @@ +import io +import json +import glob +from PIL import Image +import xml.etree.ElementTree as ET +import tensorflow as tf +import numpy as np +import cv2 +import pandas as pd + +class DataAnnotator(object): + def __init__(self, classes): + self.classes = classes # array of class labels + + def list_to_csv(self, annotations, outfile): + columns = ['filename', 'width', 'height', 'class', 'xmin', 'ymin', 'xmax', 'ymax'] + xml_df = pd.DataFrame(annotations, columns=columns) + xml_df.to_csv(outfile, index=None) + + def class_to_int(self, class): + for i in range(len(self.classes)): + if self.classes[i] == class: + return i + 1 + else: + return None + + def xml_to_csv(self, xml_path, csv_path): + annotations = [] + for xml_file in glob.glob(xml_path + '*.xml'): + tree = ET.parse(xml_file) + root = tree.getroot() + for member in root.findall('object'): + value = (root.find('filename').text, + int(root.find('size')[0].text), int(root.find('size')[1].text), + member[0].text, + int(member[4][0].text), int(member[4][1].text), + int(member[4][2].text), int(member[4][3].text)) + annotations.append(value) + + self.list_to_csv(annotations, csv_path) + + def json_to_csv(self, jsonpath, csvpath): + with open(jsonpath) as f: + images = json.load(f) + + annotations = [] + + for entry in images: + filename = images[entry]['filename'] + for region in images[entry]['regions']: + c = region['region_attributes']['class'] + xmin = region['shape_attributes']['x'] + ymin = region['shape_attributes']['y'] + xmax = xmin + region['shape_attributes']['width'] + ymax = ymin + region['shape_attributes']['height'] + width = 0 + height = 0 + + value = (filename, width, height, c, xmin, ymin, xmax, ymax) + annotations.append(value) + + self.list_to_csv(annotations, csvpath) + + def generate_tfexample(self, group, path): + with tf.gfile.GFile(os.path.join(path, '{}'.format(group.filename)), 'rb') as fid: + encoded_jpg = fid.read() + encoded_jpg_io = io.BytesIO(encoded_jpg) + image = Image.open(encoded_jpg_io) + width, height = image.size + + filename = group.filename.encode('utf8') + image_format = b'jpg' + xmins = [] + xmaxs = [] + ymins = [] + ymaxs = [] + classes_text = [] + classes = [] + + for index, row in group.object.iterrows(): + xmins.append(row['xmin'] / width) + xmaxs.append(row['xmax'] / width) + ymins.append(row['ymin'] / height) + ymaxs.append(row['ymax'] / height) + classes_text.append(row['class'].encode('utf8')) + classes.append(self.class_to_int(row['class'])) + + tf_example = tf.train.Example(features=tf.train.Features(feature={ + 'image/height': dataset_util.int64_feature(height), + 'image/width': dataset_util.int64_feature(width), + 'image/filename': dataset_util.bytes_feature(filename), + 'image/source_id': dataset_util.bytes_feature(filename), + 'image/encoded': dataset_util.bytes_feature(encoded_jpg), + 'image/format': dataset_util.bytes_feature(image_format), + 'image/object/bbox/xmin': dataset_util.float_list_feature(xmins), + 'image/object/bbox/xmax': dataset_util.float_list_feature(xmaxs), + 'image/object/bbox/ymin': dataset_util.float_list_feature(ymins), + 'image/object/bbox/ymax': dataset_util.float_list_feature(ymaxs), + 'image/object/class/text': dataset_util.bytes_list_feature(classes_text), + 'image/object/class/label': dataset_util.int64_list_feature(classes), + })) + + return tf_example + + def csv_to_tfrecord(self, csvpath, filename, tfrpath): + csv = pd.read_csv(csvpath).values \ No newline at end of file diff --git a/sightseer/sightseer.py b/sightseer/sightseer.py new file mode 100644 index 0000000..a684a95 --- /dev/null +++ b/sightseer/sightseer.py @@ -0,0 +1,125 @@ +import cv2 +import numpy as np +import tensorflow as tf +from tensorflow.keras.preprocessing.image import load_img, img_to_array +from PIL import ImageGrab +import matplotlib.pyplot as plt +from matplotlib.patches import Rectangle + +class Sightseer(object): + def __init__(self): + self.filepath = None + + def render_grayscale(self, frame): + gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) + return gray_frame + + def load_webcam(self, return_data=True, set_gray=True, kill_key="q", width=160, height=120): + + cap = cv2.VideoCapture(0) + cap.set(cv2.CAP_PROP_FRAME_WIDTH, width) + cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height) + + frames = [] + + while True: + ret, frame = cap.read() + print (frame.shape) + + if set_gray: + frame = self.render_grayscale(frame) + + frame = cv2.flip(frame, 1) # prevent lateral inversion + cv2.imshow('frame', frame) + frames.append(frame) + + if cv2.waitKey(1) & 0xFF == ord(kill_key): + break + + cap.release() + cv2.destroyAllWindows() + + if return_data: + frames = np.array(frames) + return frames + + def screen_grab(self, set_gray=True, write_data=True, return_data=True, kill_key="q", filename='output.avi', width=400, height=400): + fourcc = cv2.VideoWriter_fourcc(*'XVID') + out = cv2.VideoWriter(filename, fourcc, 20.0, (640, 480)) + + frames = [] + + while True: + img = np.array(ImageGrab.grab(bbox=(0, 0, width, height))) + frame = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) + + if write_data: + out.write(imcv) + + if set_gray: + frame = self.render_grayscale(img) + + cv2.imshow('frame', frame) + frames.append(frame) + + if cv2.waitKey(1) & 0xFF == ord(kill_key): + break + + out.release() + cv2.destroyAllWindows() + + if return_data: + frames = np.array(frames) + return frames + + def load_vidsource(self, filepath, return_data=True, set_gray=True, kill_key="q"): + self.filepath = filepath + vidcap = cv2.VideoCapture(filepath) + + frame_exists, frame = vidcap.read() + frames = [] + + while frame_exists: + frame_exists, frame = vidcap.read() + print (frame.shape) + + if set_gray: + frame = self.render_grayscale(frame) + + cv2.imshow('frame', frame) + frames.append(frame) + + if cv2.waitKey(1) & 0xFF == ord(kill_key): + break + + vidcap.release() + cv2.destroyAllWindows() + + if return_data: + frames = np.array(frames) + return frames + + def load_image(self, filepath): + self.filepath = filepath + try: + img = cv2.imread(filepath) + return img + except: + raise FileExistsError ("File does not exist. You may want to check the filepath again.") + + def get_final_filepath(self, image_path): + image_path = image_path.split('/') + img_name = image_path[-1] + img_name = img_name.split('.') + img_name = img_name[0] + "_detected." + img_name[1] + image_path = "/".join(image_path[:-1]) + "/" + img_name + + return image_path + + def render_image(self, image, save_image=False): + plt.imshow(image) + plt.show() + + if save_image: + new_filepath = self.get_final_filepath(self.filepath) + plt.savefig(new_filepath) \ No newline at end of file diff --git a/sightseer/zoo.py b/sightseer/zoo.py new file mode 100644 index 0000000..7dcf84e --- /dev/null +++ b/sightseer/zoo.py @@ -0,0 +1,369 @@ +import os +import wget +import struct +import shutil +import logging + +import cv2 +import numpy as np +import tensorflow as tf +from tensorflow.keras.layers import Input, UpSampling2D, concatenate +from tensorflow.keras.models import Model + +from .blocks import ConvBlock, BoundingBox, SightLoader + +# disabling warnings and logging +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' +tf.autograph.set_verbosity(tf.compat.v1.logging.ERROR) +logging.disable(logging.WARNING) + +class YOLOv3Client(object): + def __init__(self, nms_threshold=0.45, obj_threshold=0.5, net_h=416, net_w=416, anchors=[[116, 90, 156, 198, 373, 326], [30, 61, 62, 45, 59, 119], [10, 13, 16, 30, 33, 23]]): + """ + Params: + ------- + + - nsm_threshold (float): Non Maximum Suppression threshold for selecting bounding boxes ina region + default: 0.45 + min: 0 + max: 1 + + - obj_threshold (float): + default: 0.5 + min: 0 + max: 1 + + - + """ + self.nms_threshold = nms_threshold + self.obj_threshold = obj_threshold + self.net_h, self.net_w = net_h, net_w + self.anchors = anchors + self.yolo_model = None + self.all_labels = ["person", "bicycle", "car", "motorbike", "aeroplane", "bus", "train", "truck", + "boat", "traffic light", "fire hydrant", "stop sign", "parking meter", "bench", + "bird", "cat", "dog", "horse", "sheep", "cow", "elephant", "bear", "zebra", "giraffe", + "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee", "skis", "snowboard", + "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard", + "tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", + "apple", "sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", + "chair", "sofa", "pottedplant", "bed", "diningtable", "toilet", "tvmonitor", "laptop", "mouse", + "remote", "keyboard", "cell phone", "microwave", "oven", "toaster", "sink", "refrigerator", + "book", "clock", "vase", "scissors", "teddy bear", "hair drier", "toothbrush"] + + def download_weights(self): + """ + Downloads the weights from online and saves them locally + """ + + if os.path.exists("./bin/yolov3.weights"): + print ("Weights already exist. Proceeding to load YOLOv3Client...") + else: + print ("Downloading weights. This may may take a moment...") + weights_url = "https://pjreddie.com/media/files/yolov3.weights" + # config_url = "https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/yolov3.cfg" + + wget.download(weights_url, os.getcwd() + "/yolov3.weights") + # wget.download(config_url, os.getcwd() + "/yolov3.cfg") + + os.mkdir("./bin", 0o755) # configuring admin rights + shutil.move("./yolov3.weights", "./bin/yolov3.weights") + # shutil.move("./yolov3.cfg", "./bin/yolov3.cfg") + + print ("\n\nWeights downloaded successfully!") + + def load_architecture(self): + """ + Returns tf.keras.models.Model instance + """ + inp_image = Input(shape=[None, None, 3]) + + x = ConvBlock.get_conv_block(inp_image, [{'filter': 32, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 0}, + {'filter': 64, 'kernel': 3, 'stride': 2, 'bnorm': True, 'leaky': True, 'layer_idx': 1}, + {'filter': 32, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 2}, + {'filter': 64, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 3}]) + + x = ConvBlock.get_conv_block(x, [{'filter': 128, 'kernel': 3, 'stride': 2, 'bnorm': True, 'leaky': True, 'layer_idx': 5}, + {'filter': 64, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 6}, + {'filter': 128, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 7}]) + + x = ConvBlock.get_conv_block(x, [{'filter': 64, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 9}, + {'filter': 128, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 10}]) + + x = ConvBlock.get_conv_block(x, [{'filter': 256, 'kernel': 3, 'stride': 2, 'bnorm': True, 'leaky': True, 'layer_idx': 12}, + {'filter': 128, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 13}, + {'filter': 256, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 14}]) + + for i in range(7): + x = ConvBlock.get_conv_block(x, [{'filter': 128, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 16+i*3}, + {'filter': 256, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 17+i*3}]) + + skip_36 = x + + x = ConvBlock.get_conv_block(x, [{'filter': 512, 'kernel': 3, 'stride': 2, 'bnorm': True, 'leaky': True, 'layer_idx': 37}, + {'filter': 256, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 38}, + {'filter': 512, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 39}]) + + for i in range(7): + x = ConvBlock.get_conv_block(x, [{'filter': 256, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 41+i*3}, + {'filter': 512, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 42+i*3}]) + + skip_61 = x + + x = ConvBlock.get_conv_block(x, [{'filter': 1024, 'kernel': 3, 'stride': 2, 'bnorm': True, 'leaky': True, 'layer_idx': 62}, + {'filter': 512, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 63}, + {'filter': 1024, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 64}]) + + for i in range(3): + x = ConvBlock.get_conv_block(x, [{'filter': 512, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 66+i*3}, + {'filter': 1024, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 67+i*3}]) + + x = ConvBlock.get_conv_block(x, [{'filter': 512, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 75}, + {'filter': 1024, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 76}, + {'filter': 512, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 77}, + {'filter': 1024, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 78}, + {'filter': 512, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 79}], skip=False) + + yolo_82 = ConvBlock.get_conv_block(x, [{'filter': 1024, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 80}, + {'filter': 255, 'kernel': 1, 'stride': 1, 'bnorm': False, 'leaky': False, 'layer_idx': 81}], skip=False) + + x = ConvBlock.get_conv_block(x, [{'filter': 256, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 84}], skip=False) + x = UpSampling2D(2)(x) + x = concatenate([x, skip_61]) + + x = ConvBlock.get_conv_block(x, [{'filter': 256, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 87}, + {'filter': 512, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 88}, + {'filter': 256, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 89}, + {'filter': 512, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 90}, + {'filter': 256, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 91}], skip=False) + + yolo_94 = ConvBlock.get_conv_block(x, [{'filter': 512, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 92}, + {'filter': 255, 'kernel': 1, 'stride': 1, 'bnorm': False, 'leaky': False, 'layer_idx': 93}], skip=False) + + x = ConvBlock.get_conv_block(x, [{'filter': 128, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 96}], skip=False) + x = UpSampling2D(2)(x) + x = concatenate([x, skip_36]) + + yolo_106 = ConvBlock.get_conv_block(x, [{'filter': 128, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 99}, + {'filter': 256, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 100}, + {'filter': 128, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 101}, + {'filter': 256, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 102}, + {'filter': 128, 'kernel': 1, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 103}, + {'filter': 256, 'kernel': 3, 'stride': 1, 'bnorm': True, 'leaky': True, 'layer_idx': 104}, + {'filter': 255, 'kernel': 1, 'stride': 1, 'bnorm': False, 'leaky': False, 'layer_idx': 105}], skip=False) + + model = Model(inp_image, [yolo_82, yolo_94, yolo_106]) + return model + + def sigmoid(self, z): + return 1 / (1 + np.exp(-z)) + + def preprocess(self, image): + """ + Resizes image to appropriate dimensions for YOLOv3 + """ + new_h, new_w, _ = image.shape + + if (float(self.net_w)/new_w) < (float(self.net_h)/new_h): + new_h = (new_h * self.net_w)//new_w + new_w = self.net_w + else: + new_w = (new_w * self.net_h)//new_h + new_h = self.net_h + + # resize the image to the new size + resized = cv2.resize(image[:, :, ::-1]/255., (int(new_w), int(new_h))) + + # embed the image into the standard letter box + new_img = np.ones((self.net_h, self.net_w, 3)) * 0.5 + new_img[int((self.net_h-new_h)//2):int((self.net_h+new_h)//2), int((self.net_w-new_w)//2):int((self.net_w+new_w)//2), :] = resized + new_img = np.expand_dims(new_img, 0) + + return new_img + + def interval_overlap(self, int_a, int_b): + x1, x2 = int_a + x3, x4 = int_b + + if x3 < x1: + if x4 < x1: + return 0 + else: + return min(x2, x4) - x1 + else: + if x2 < x3: + return 0 + else: + return min(x2, x4) - x3 + + def bbox_iou(self, box1, box2): + """ + Finds IOU between all bounding boxes before non maximum suppression process + """ + int_w = self.interval_overlap([box1.xmin, box1.xmax], [box2.xmin, box2.xmax]) + int_h = self.interval_overlap([box1.ymin, box1.ymax], [box2.ymin, box2.ymax]) + + intersect = int_w * int_h + + w1, h1 = box1.xmax - box1.xmin, box1.ymax - box1.ymin + w2, h2 = box2.xmax - box2.xmin, box2.ymax - box2.ymin + + union = w1*h1 + w2*h2 - intersect + + return float(intersect) / union + + def non_maximum_suppression(self, boxes): + if len(boxes) > 0: + nb_class = len(boxes[0].classes) + else: + return + + for c in range(nb_class): + sorted_indices = np.argsort([-box.classes[c] for box in boxes]) + + for i in range(len(sorted_indices)): + index_i = sorted_indices[i] + + if boxes[index_i].classes[c] == 0: continue + + for j in range(i+1, len(sorted_indices)): + index_j = sorted_indices[j] + + if self.bbox_iou(boxes[index_i], boxes[index_j]) >= self.nms_threshold: + boxes[index_j].classes[c] = 0 + + return boxes + + def decode_preds(self, preds, anchors): + gridh, gridw = preds.shape[:2] + nb_box = 3 + preds = preds.reshape([gridh, gridw, nb_box, -1]) + nb_class = preds.shape[-1] - 5 + + boxes = [] + + preds[..., :2] = self.sigmoid(preds[..., :2]) + preds[..., 4:] = self.sigmoid(preds[..., 4:]) + preds[..., 5:] = preds[..., 4][..., np.newaxis] * preds[..., 5:] + preds[..., 5:] *= preds[..., 5:] > self.obj_threshold + + for i in range(gridh * gridw): + row = i / gridw + col = i % gridw + + for b in range(nb_box): + objectness = preds[int(row)][int(col)][b][4] + + if (objectness.all() <= self.obj_threshold): continue + + x, y, w, h = preds[int(row)][int(col)][b][:4] + + x = (col + x) / gridw + y = (row + y) / gridh + w = anchors[2 * b + 0] * np.exp(w) / self.net_w + h = anchors[2 * b + 1] * np.exp(h) / self.net_h + + classes = preds[int(row)][col][b][5:] + + box = BoundingBox(x-w/2, y-h/2, x+w/2, y+h/2, objectness, classes) + + boxes.append(box) + + return boxes + + def rectify_boxes(self, boxes, image_h, image_w): + if (float(self.net_w)/image_w) < (float(self.net_h)/image_h): + new_w = self.net_w + new_h = (image_h * self.net_w)/ image_w + else: + new_h = self.net_w + new_w = (image_w * self.net_h) / image_h + + for i in range(len(boxes)): + x_offset, x_scale = (self.net_w - new_w)/2./self.net_w, float(new_w)/self.net_w + y_offset, y_scale = (self.net_h - new_h)/2./self.net_h, float(new_h)/self.net_h + + boxes[i].xmin = int((boxes[i].xmin - x_offset) / x_scale * image_w) + boxes[i].xmax = int((boxes[i].xmax - x_offset) / x_scale * image_w) + boxes[i].ymin = int((boxes[i].ymin - y_offset) / y_scale * image_h) + boxes[i].ymax = int((boxes[i].ymax - y_offset) / y_scale * image_h) + + return boxes + + def get_boxes(self, image, boxes, verbose=True, random_coloring=True): + final_boxes = [] + + for box in boxes: + final_label = "" + label = -1 + + for i in range(len(self.all_labels)): + if box.classes[i] > self.obj_threshold: + final_label += self.all_labels[i] + label = i + + if verbose: + print ("{}: {:.3f}%".format(self.all_labels[i], box.classes[i]*100)) + + final_boxes.append([final_label, + box.classes[i] * 100, + { + 'xmin': box.xmin, + 'ymin': box.ymin, + 'xmax': box.xmax, + 'ymax': box.ymax + } + ]) + + if label >= 0: + if random_coloring: + r, g, b = np.random.randint(0, 255), np.random.randint(0, 255), np.random.randint(0, 255) + else: + r, g, b = 0, 255, 0 + + cv2.rectangle(image, (box.xmin, box.ymin), (box.xmax, box.ymax), (r, g, b), 1) + cv2.putText(image, '{} {:.3f}'.format(final_label, box.get_confidence()), (box.xmax, box.ymin - 13), cv2.FONT_HERSHEY_SIMPLEX, 1e-3 * image.shape[0], (r, g, b), 2) + + return final_boxes, image + + def load_model(self, default_path="./bin/yolov3.weights", verbose=True): + """ + Downloads weights and config, loads checkpoints into architecture + """ + self.download_weights() # downloading weights from online + loader = SightLoader(default_path) + + self.yolo_model = self.load_architecture() # loading weights into model + loader.load_weights(self.yolo_model, verbose) + + def predict(self, original_image, return_img=False, verbose=True): + """ + Returns a list of BoundingBox metadata (class label, confidence score, coordinates) + and the edited image with bounding boxes and their corresponding text labels/confidence scores + """ + image_h, image_w = original_image.shape[:2] + + if self.yolo_model == None: + raise ValueError ("YOLOv3 weights needs to be downloaded and configured into the model before use. You can use the `load_model()` method to do so.") + + proc_image = self.preprocess(original_image) + preds = self.yolo_model.predict(proc_image) + boxes = [] + + for i in range(len(preds)): + boxes += self.decode_preds(preds[i][0], self.anchors[i]) + + boxes = self.rectify_boxes(boxes, image_h, image_w) + boxes = self.non_maximum_suppression(boxes) + + box_list, box_image = self.get_boxes(original_image, boxes, verbose) + + if return_img: + box_image = box_image.squeeze() + return box_list, box_image + else: + return box_list + +class MaskRCNNClient(object): + def __init__(self): + pass \ No newline at end of file