├── .gitignore ├── LICENSE ├── README.md ├── docs └── img │ ├── astronaut.jpg │ ├── excavator.jpg │ ├── genericObjectLocalizer.gif │ ├── map.jpg │ ├── output.jpg │ └── phone accessories.jpg ├── download_model.py ├── genericDetector ├── __init__.py └── genericDetector.py ├── imageObjectDetection.py ├── models └── .gitkeep ├── requirements.txt ├── videoObjectDetection.py └── webcamObjectDetection.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ibai Gorordo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tensorflow-Mobile-Generic-Object-Localizer 2 | Python Tensorflow 2 scripts for detecting objects of any class in an image without knowing their label. 3 | 4 | ![Tensorflow Generic Object Localizer](https://github.com/ibaiGorordo/Tensorflow-Mobile-Generic-Object-Localizer/blob/main/docs/img/output.jpg) 5 | *Original image taken from the OpenCV AI Kit - Lite, make sure to check it out: https://www.kickstarter.com/projects/opencv/opencv-ai-kit-oak-depth-camera-4k-cv-edge-object-detection* 6 | 7 | ### :exclamation::warning:The object **detector works better with images with few objects** and it starts to fail in more complex scenes. The model is suitable for automatically labelling objects for custom object detection models. 8 | 9 | # Requirements 10 | 11 | * **OpenCV**, **imread-from-url** and **tensorflow**. Also, **pafy** and **youtube-dl** are required for youtube video inference. 12 | 13 | # Installation 14 | ``` 15 | pip install -r requirements.txt 16 | pip install pafy youtube-dl 17 | ``` 18 | 19 | # Tensorflow model 20 | The original models was taken from [Tensorflow Hub](https://tfhub.dev/google/object_detection/mobile_object_localizer_v1/1), download it, and place it in the **[models folder](https://github.com/ibaiGorordo/Tensorflow-Mobile-Generic-Object-Localizer/tree/main/models)**. 21 | 22 | Use the following script to download the model: 23 | ``` 24 | python download_model.py 25 | ``` 26 | 27 | 28 | # Examples 29 | 30 | * **Image inference**: 31 | 32 | ``` 33 | python imageObjectDetection.py 34 | ``` 35 | 36 | * **Webcam inference**: 37 | 38 | ``` 39 | python webcamObjectDetection.py 40 | ``` 41 | 42 | * **Video inference**: 43 | 44 | ``` 45 | python videoObjectDetection.py 46 | ``` 47 | 48 | # Inference Examples 49 | ![Generic object detector figures](https://github.com/ibaiGorordo/Tensorflow-Mobile-Generic-Object-Localizer/blob/main/docs/img/genericObjectLocalizer.gif) 50 | 51 | *Original video by Animist: https://youtu.be/uKyoV0uG9rQ* 52 | 53 | ## Astronaut detection 54 | ![Astrounaut Tensorflow detection](https://github.com/ibaiGorordo/Tensorflow-Mobile-Generic-Object-Localizer/blob/main/docs/img/astronaut.jpg) 55 | *Original image: https://commons.wikimedia.org/wiki/File:Astronaut_Standing_On_The_Moon.png* 56 | 57 | ## Excavator detection 58 | ![Excabator Tensorflow detection](https://github.com/ibaiGorordo/Tensorflow-Mobile-Generic-Object-Localizer/blob/main/docs/img/excavator.jpg) 59 | *Original image: https://en.wikipedia.org/wiki/Hitachi_Construction_Machinery_(Europe)#/media/File:ZX350LCN-3-Photo28-lo.jpg* 60 | 61 | ## Map island detection 62 | ![Map island Tensorflow detection](https://github.com/ibaiGorordo/Tensorflow-Mobile-Generic-Object-Localizer/blob/main/docs/img/map.jpg) 63 | *Original image: https://ja.m.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB:Map_of_Hawaii_highlighting_Hawaii_(island).svg* 64 | 65 | ## Phone accessories detection 66 | ![Phone accesories Tensorflow detection](https://github.com/ibaiGorordo/Tensorflow-Mobile-Generic-Object-Localizer/blob/main/docs/img/phone%20accessories.jpg) 67 | *Original image: https://upload.wikimedia.org/wikipedia/commons/thumb/1/1b/OnePlus_3_phone%2C_charger_and_package.jpg/1024px-OnePlus_3_phone%2C_charger_and_package.jpg* 68 | 69 | ## And many more 70 | 71 | # References: 72 | * Original model: https://tfhub.dev/google/object_detection/mobile_object_localizer_v1/1 73 | -------------------------------------------------------------------------------- /docs/img/astronaut.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibaiGorordo/Tensorflow-Mobile-Generic-Object-Localizer/25d158c53547542913ad287e19e20273cf82c33e/docs/img/astronaut.jpg -------------------------------------------------------------------------------- /docs/img/excavator.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibaiGorordo/Tensorflow-Mobile-Generic-Object-Localizer/25d158c53547542913ad287e19e20273cf82c33e/docs/img/excavator.jpg -------------------------------------------------------------------------------- /docs/img/genericObjectLocalizer.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibaiGorordo/Tensorflow-Mobile-Generic-Object-Localizer/25d158c53547542913ad287e19e20273cf82c33e/docs/img/genericObjectLocalizer.gif -------------------------------------------------------------------------------- /docs/img/map.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibaiGorordo/Tensorflow-Mobile-Generic-Object-Localizer/25d158c53547542913ad287e19e20273cf82c33e/docs/img/map.jpg -------------------------------------------------------------------------------- /docs/img/output.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibaiGorordo/Tensorflow-Mobile-Generic-Object-Localizer/25d158c53547542913ad287e19e20273cf82c33e/docs/img/output.jpg -------------------------------------------------------------------------------- /docs/img/phone accessories.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibaiGorordo/Tensorflow-Mobile-Generic-Object-Localizer/25d158c53547542913ad287e19e20273cf82c33e/docs/img/phone accessories.jpg -------------------------------------------------------------------------------- /download_model.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import tarfile 3 | 4 | url = "https://tfhub.dev/google/object_detection/mobile_object_localizer_v1/1?tf-hub-format=compressed" 5 | response = requests.get(url, stream=True) 6 | file = tarfile.open(fileobj=response.raw, mode="r|gz") 7 | file.extractall(path="models/saved_model") -------------------------------------------------------------------------------- /genericDetector/__init__.py: -------------------------------------------------------------------------------- 1 | from .genericDetector import GenericDetector -------------------------------------------------------------------------------- /genericDetector/genericDetector.py: -------------------------------------------------------------------------------- 1 | import time 2 | import cv2 3 | import numpy as np 4 | import tensorflow as tf 5 | from imread_from_url import imread_from_url 6 | 7 | np.random.seed(0) 8 | np.random.random(9) 9 | np.random.random(15) 10 | np.random.random(2021) 11 | 12 | colors = np.random.randint(255, size=(100, 3), dtype=int) 13 | 14 | class GenericDetector(): 15 | 16 | def __init__(self, model_path, threshold = 0.2): 17 | 18 | self.threshold = threshold 19 | 20 | # Initialize model 21 | self.initialize_model(model_path) 22 | 23 | def __call__(self, image): 24 | 25 | return self.detect_objects(image) 26 | 27 | def initialize_model(self, model_path): 28 | 29 | self.model = tf.saved_model.load(model_path).signatures['default'] 30 | 31 | # Get model info 32 | self.getModel_input_details() 33 | 34 | def detect_objects(self, image): 35 | 36 | input_tensor = self.prepare_input(image) 37 | 38 | # Perform inference on the image 39 | output = self.inference(input_tensor) 40 | 41 | # # Process output data 42 | detections = self.process_output(output) 43 | 44 | return detections 45 | 46 | def prepare_input(self, image): 47 | 48 | img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) 49 | 50 | input_tensor = cv2.resize(img, (self.input_width,self.input_height)) 51 | input_tensor = input_tensor[np.newaxis,:,:,:] 52 | 53 | return tf.convert_to_tensor(input_tensor.astype(np.float32)) 54 | 55 | def inference(self, input_tensor): 56 | 57 | # Peform inference 58 | return self.model(input_tensor) 59 | 60 | def process_output(self, output): 61 | 62 | # Get all output details 63 | boxes = output['detection_boxes'].numpy()[0] 64 | classes = output['detection_classes'].numpy()[0] 65 | scores = output['detection_scores'].numpy()[0] 66 | num_objects = int(output['num_detections'][0]) 67 | 68 | results = [] 69 | for i in range(num_objects): 70 | if scores[i] >= self.threshold: 71 | result = { 72 | 'bounding_box': boxes[i], 73 | 'class_id': classes[i], 74 | 'score': scores[i] 75 | } 76 | results.append(result) 77 | return results 78 | 79 | def getModel_input_details(self): 80 | 81 | input_shape = self.model.inputs[0].shape 82 | self.input_height = input_shape[1] 83 | self.input_width = input_shape[2] 84 | self.channels = input_shape[3] 85 | 86 | @staticmethod 87 | def draw_detections(image, detections): 88 | 89 | img_height, img_width, _ = image.shape 90 | 91 | for idx, detection in enumerate(detections): 92 | box = detection['bounding_box'] 93 | y1 = (img_height * box[0]).astype(int) 94 | y2 = (img_height * box[2]).astype(int) 95 | x1 = (img_width * box[1]).astype(int) 96 | x2 = (img_width * box[3]).astype(int) 97 | 98 | cv2.rectangle(image, (x1, y1), (x2, y2), (int(colors[idx,0]), int(colors[idx,1]), int(colors[idx,2])), 5) 99 | 100 | return image 101 | 102 | 103 | if __name__ == '__main__': 104 | 105 | model_path="models/saved_model" 106 | threshold = 0.2 107 | 108 | # Initialize object detection model 109 | detector = GenericDetector(model_path, threshold) 110 | 111 | # Read RGB image 112 | image = imread_from_url("https://ksr-ugc.imgix.net/assets/034/889/438/46e41611066c0eeae3c25773e499e926_original.png?ixlib=rb-4.0.2&crop=faces&w=1024&h=576&fit=crop&v=1631721168&auto=format&frame=1&q=92&s=9ce81981923cea116129532639be5d37") 113 | 114 | # Draw the detected objects 115 | detections = detector(image) 116 | detection_img = detector.draw_detections(image, detections) 117 | 118 | cv2.namedWindow("Detections", cv2.WINDOW_NORMAL) 119 | cv2.imshow("Detections", detection_img) 120 | cv2.waitKey(0) 121 | -------------------------------------------------------------------------------- /imageObjectDetection.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | from imread_from_url import imread_from_url 3 | 4 | from genericDetector import GenericDetector 5 | 6 | 7 | model_path="models/saved_model" 8 | threshold = 0.2 9 | 10 | # Initialize object detection model 11 | detector = GenericDetector(model_path, threshold) 12 | 13 | # Read RGB image 14 | image = imread_from_url("https://ksr-ugc.imgix.net/assets/034/889/438/46e41611066c0eeae3c25773e499e926_original.png?ixlib=rb-4.0.2&crop=faces&w=1024&h=576&fit=crop&v=1631721168&auto=format&frame=1&q=92&s=9ce81981923cea116129532639be5d37") 15 | 16 | # Draw the detected objects 17 | detections = detector(image) 18 | detection_img = detector.draw_detections(image, detections) 19 | 20 | cv2.namedWindow("Detections", cv2.WINDOW_NORMAL) 21 | cv2.imshow("Detections", detection_img) 22 | cv2.waitKey(0) 23 | 24 | cv2.imwrite("output.jpg",detection_img) -------------------------------------------------------------------------------- /models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibaiGorordo/Tensorflow-Mobile-Generic-Object-Localizer/25d158c53547542913ad287e19e20273cf82c33e/models/.gitkeep -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | opencv-python 2 | imread-from-url 3 | tensorflow -------------------------------------------------------------------------------- /videoObjectDetection.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import pafy 3 | from genericDetector import GenericDetector 4 | 5 | model_path="models/saved_model" 6 | threshold = 0.3 7 | 8 | # out = cv2.VideoWriter('outpy2.avi',cv2.VideoWriter_fourcc('M','J','P','G'), 30, (720,720)) 9 | 10 | # Initialize video 11 | # cap = cv2.VideoCapture("video.avi") 12 | 13 | videoUrl = 'https://youtu.be/uKyoV0uG9rQ' 14 | videoPafy = pafy.new(videoUrl) 15 | print(videoPafy.streams) 16 | cap = cv2.VideoCapture(videoPafy.streams[-1].url) 17 | 18 | # Initialize object detection model 19 | detector = GenericDetector(model_path, threshold) 20 | 21 | cv2.namedWindow("Detections", cv2.WINDOW_NORMAL) 22 | cap.set(cv2.CAP_PROP_POS_FRAMES, 60) 23 | while cap.isOpened(): 24 | try: 25 | # Read frame from the video 26 | ret, frame = cap.read() 27 | except: 28 | continue 29 | 30 | if ret: 31 | 32 | # Draw the detected objects 33 | detections = detector(frame) 34 | detection_img = detector.draw_detections(frame, detections) 35 | 36 | out.write(detection_img) 37 | cv2.imshow("Detections", detection_img) 38 | 39 | else: 40 | break 41 | 42 | # Press key q to stop 43 | if cv2.waitKey(1) == ord('q'): 44 | break 45 | 46 | cap.release() 47 | # out.release() 48 | cv2.destroyAllWindows() -------------------------------------------------------------------------------- /webcamObjectDetection.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | 3 | from genericDetector import GenericDetector 4 | 5 | model_path="models/saved_model" 6 | threshold = 0.3 7 | 8 | # Initialize object detection model 9 | detector = GenericDetector(model_path, threshold) 10 | 11 | # Initialize webcam 12 | cap = cv2.VideoCapture(0) 13 | cv2.namedWindow("Detections", cv2.WINDOW_NORMAL) 14 | 15 | while(True): 16 | ret, frame = cap.read() 17 | 18 | # Draw the detected objects 19 | detections = detector(frame) 20 | detection_img = detector.draw_detections(frame, detections) 21 | 22 | 23 | cv2.imshow("Detections", detection_img) 24 | if cv2.waitKey(1) & 0xFF == ord('q'): 25 | break --------------------------------------------------------------------------------