{"id":580,"date":"2021-07-12T12:00:01","date_gmt":"2021-07-12T10:00:01","guid":{"rendered":"https:\/\/www.iot-embedded.de\/iot-2021\/?p=580"},"modified":"2021-07-12T12:00:03","modified_gmt":"2021-07-12T10:00:03","slug":"sicherheitskamera-featuring-machine-learning-umsetzung-der-objekterkennung","status":"publish","type":"post","link":"https:\/\/www.iot-embedded.de\/iot-2021\/smart-security\/sicherheitskamera-featuring-machine-learning-umsetzung-der-objekterkennung\/","title":{"rendered":"Sicherheitskamera featuring Machine Learning &#8211; Umsetzung der Objekterkennung"},"content":{"rendered":"\n<p>In diesem Blogeintrag setzen wir die Objekterkennung mit dem im <a href=\"https:\/\/www.iot-embedded.de\/iot-2021\/smart-security\/sicherheitskamera-featuring-machine-learning-auswahl-der-erkennungsmethode\/\">vorherigen Blogeintrag <\/a>ausgew\u00e4hlten SSD Model um. F\u00fcr die Umsetzung ben\u00f6tigen wir das <a href=\"https:\/\/github.com\/Phape\/smart-security\/tree\/master\/device\/camera\/model\">Caffeemodell und die Modellstruktur<\/a> des MobileNet SSD, die Bibliothek OpenCV, sowie verschiedene Bibliotheken f\u00fcr die Bild- und Videoverarbeitung<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Dockerfile<\/h2>\n\n\n\n<p>Damit die Objekterkennung in einem eigenen Service startet ist es n\u00f6tig folgendes Dockerfile zu erstellen. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Fehlende Dependencies f\u00fcr Raspi4 images daher raspbi 3\nFROM balenalib\/raspberrypi3-debian:stretch-20190612\n\n# Install dependencies\nRUN apt-get update &amp;&amp; \\\n  apt-get install -yq \\\n    python3 \\\n    python3-dev \\\n    python3-pip \\\n    python3-setuptools \\\n    gstreamer-1.0 \\\n    v4l-utils \\\n    libopus-dev \\\n    libvpx-dev \\\n    libsrtp2-dev \\\n    libopencv-dev \\\n    libatlas3-base \\\n    libatlas-base-dev \\\n    libjasper-dev \\\n    libilmbase12 \\\n    libopenexr22 \\\n    libavformat-dev \\\n    libswscale-dev \\\n    libqtgui4 \\\n    libqt4-test \\\n    libavdevice-dev \\\n    libavfilter-dev \\\n    libavcodec-dev \\\n    libgtk-3-dev \\\n  &amp;&amp; apt-get clean &amp;&amp; rm -rf \/var\/lib\/apt\/lists\/*\n\nWORKDIR \/usr\/src\/app\n# Enable the v4l2 driver for the Raspberry Pi camera\nRUN printf \"bcm2835-v4l2\\n\" &gt;&gt; \/etc\/modules\n\nRUN pip3 install --upgrade pip\n\nCOPY requirements.txt .\nRUN pip3 install --no-cache-dir -r requirements.txt\n\nCOPY app.conf .\/\nCOPY .\/src .\/\nCOPY .\/model .\/\n\nCMD &#091;\"python3\", \"app.py\"]<\/code><\/pre>\n\n\n\n<p>Die Installation der OpenCV Bibliothek ist entweder durch die Nutzung des Package Installers pip oder durch die eigene Kompilierung auf dem Endger\u00e4t m\u00f6glich. Da das Kompilieren von OpenCV auf einem Raspberry Pi bis zu einer Stunde dauern kann verwenden wir die Installation \u00fcber pip. Die pip Installation von OpenCV bringt jedoch ebenfalls eigene Nachteile mit sich. Durch die pip Installation ist es nicht mehr m\u00f6glich selbstst\u00e4ndig die Bibliotheken zur Bild- und Videoverarbeitung festzulegen. Manche der von der pip Installation festgelegten Bibliotheken wie beispielsweise <code>libjasper-dev<\/code> sind nicht kompatibel zu den Balena Images des Raspberry Pi 4. Daher ist es n\u00f6tig ein \u00e4lteres Base-Image des Raspberry Pi 3 zu nutzen. Anschlie\u00dfend werden in der Dockerfile die n\u00f6tigen Bibliotheken und Python 3 installiert. Nach der Installation der Bibliotheken wird das Workingdirectory gesetzt und der v4l2-Treiber aktiviert sowie die n\u00f6tigen Python Bibliotheken aus der <a href=\"https:\/\/github.com\/Phape\/smart-security\/blob\/master\/device\/camera\/requirements.txt\"><code>requirements.txt<\/code> <\/a>installiert. Abschlie\u00dfend werden die Konfigurationsdatei, der Quellcode und das SSD Model Kopiert und die <code>app.py<\/code> ausgef\u00fchrt.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">App.py<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>if __name__ == \"__main__\":\n    configfile = \"app.conf\"\n    checkDeviceReadiness()\n\n    if len(sys.argv) &gt; 1:\n        configfile = sys.argv&#091;1]\n    camera_device = CameraDevice()\n    app = App(configfile, camera_device)\n    app.main()<\/code><\/pre>\n\n\n\n<p>Wie im obenliegenden Codeblock  einsehbar wird bei der Ausf\u00fchrung des <code>app.py<\/code> codes zuerst \u00fcberpr\u00fcft ob das Skript eigenst\u00e4ndig ausgef\u00fchrt wird oder von einem anderen Skript importiert wird. Falls das Skript eigenst\u00e4ndig ausgef\u00fchrt wird ruft es die Methode <code>checkDeviceReadiness()<\/code>auf.  Die Methode \u00fcberpr\u00fcft, ob ein Videoger\u00e4t erkannt wird und ob das Betriebssystem Linux ist. Wenn das der Fall ist wird ausgegeben, dass das Videoger\u00e4t bereit ist. Ansonsten versucht die Methode den v4l2 Treiber erneut zu laden und beendet das Programm. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>def checkDeviceReadiness():\n    if not os.path.exists('\/dev\/video0') and platform.system() == 'Linux':\n        print('Video device is not ready')\n        print('Trying to load bcm2835-v4l2 driver...')\n        os.system('bash -c \"modprobe bcm2835-v4l2\"')\n        time.sleep(1)\n        sys.exit()\n    else:\n        print('Video device is ready')<\/code><\/pre>\n\n\n\n<p>Nach der \u00dcberpr\u00fcfung, ob das Videoger\u00e4t bereit ist wird ein CameraDevice erstellt. Das erstellte CameraDevice Objekt wird mir der Methode <code>__init__<\/code>mithilfe der OpenCV Bibliothek initalisiert und eine Aufnahme mit einem Buffer von einem Frame begonnen.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>def __init__(self):\n        self.cap = cv2.VideoCapture(0)\n        self.cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)\n        ret, frame = self.cap.read()\n        if not ret:\n            print('Failed to open default camera. Exiting...')\n            sys.exit()\n        self.cap.set(3, 640)\n        self.cap.set(4, 480)<\/code><\/pre>\n\n\n\n<p>Daraufhin wird die  App ausgef\u00fchrt. In der <code>__init__<\/code>-Methode der App werden zuerst der Logger konfiguriert, eine Verbindung zur Redis-Datenbank aufgebaut und das SSD Modell geladen. F\u00fcr das Laden des Modells wird die <code>load<\/code>-Methode der eigenen Klasse CaffeModelLoader genutzt. In dieser Methode wird die OpenCV <code>readNetFromCaffe<\/code>-Methode verwendet um das Modell zu laden.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class CaffeModelLoader:\t\n    @staticmethod\n    def load(proto, model):\n    \tnet = cv2.dnn.readNetFromCaffe(proto, model)\n    \treturn net<\/code><\/pre>\n\n\n\n<p>Nachdem die App initialisiert ist wird die Hauptmethode ausgef\u00fchrt. In dieser Methode wird in einer Endlosschleife \u00fcberpr\u00fcft, ob das Alarmsystem scharf gestellt ist und der Bewegungsmelder eine Bewegung erkannt hat. Falls dies Zutrifft wird eine Messung durchgef\u00fchrt und anschlie\u00dfend in der Redis-Datenbank gespeichert.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>while True:\n                if self._is_alert_system_active() and self._is_movement_detected():\n                    measurement = self._perform_measurement()\n                    self._save_measurement(measurement)\n\n                interval_seconds = self._read_measurement_interval()\n                time.sleep(interval_seconds)<\/code><\/pre>\n\n\n\n<p>Um die Messung durchzuf\u00fchren wird eine FrameProcessor Objekt initialisiert, dieser ist f\u00fcr die Konvertierung der Bilddateien zu BinaryLanguageObjects (BLOB) zust\u00e4ndig und in verbindung mit dem geladenen Modell verwendet um ein SSD Objekt zu initialisieren. Nach der Initialisierung der beiden Objekte wird der letzte Frame der PiCam als JPEG-Bild abgespeichert mit der <code><a href=\"https:\/\/github.com\/Phape\/smart-security\/blob\/71ec1c26ad51db30d93d13afc28b0c92859b18f0\/device\/camera\/src\/app.py#L36\">save_jpeg_frame<\/a><\/code>-Methode. Das gespeicherte Bild wird in die <code>detect<\/code>-Methode der SSD Klasse als Parameter mitgegeben. In der <code>detect<\/code>-Methode wird das mitgegebene Bild in ein BLOB konvertiert und daraufhin in das Neurale Netz gegeben. Die erkannten Objekte werden als Tensor in der variable <code>obj_data<\/code> gespeichert.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>def detect(self, frame):\n        blob = self.proc.get_blob(frame)\n        self.net.setInput(blob)\n        detections = self.net.forward()\n    \t# detected object count\n        k = detections.shape&#091;2]\n        obj_data = &#091;]\n        for i in np.arange(0, k):\n            obj = detections&#091;0, 0, i, :]\n            obj_data.append(obj)\n        \n        return obj_data<\/code><\/pre>\n\n\n\n<p>Da ein Tensor eine komplizierte Datenstruktur aufweist werden diese durch die <code>get_object<\/code>-Methode vereinfacht. In der <code>get_objects<\/code>-Methode werden die erkannten Objekte danach gefiltert, ob sie als Mensch erkannt wurden und die mindest Erkennungssicherheit \u00fcberschreiten. Falls dies zutrifft werden diese Objekte in einem Array zur\u00fcckgegeben. <\/p>\n\n\n\n<p>Der ganze Code f\u00fcr die Umsetzung ist auf <code><a href=\"https:\/\/github.com\/Phape\/smart-security\/blob\/master\/device\/camera\/src\/app.py\">GitHub <\/a><\/code>ver\u00f6ffentlicht.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In diesem Blogeintrag setzen wir die Objekterkennung mit dem im vorherigen Blogeintrag ausgew\u00e4hlten SSD Model um. F\u00fcr die Umsetzung ben\u00f6tigen wir das Caffeemodell und die Modellstruktur des MobileNet SSD, die Bibliothek OpenCV, sowie verschiedene Bibliotheken f\u00fcr die Bild- und Videoverarbeitung<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[9],"tags":[],"_links":{"self":[{"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/posts\/580"}],"collection":[{"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/comments?post=580"}],"version-history":[{"count":5,"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/posts\/580\/revisions"}],"predecessor-version":[{"id":610,"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/posts\/580\/revisions\/610"}],"wp:attachment":[{"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/media?parent=580"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/categories?post=580"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/tags?post=580"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}