#Lemoncode – Hola #Kubernetes: Definiciones

Buy Me A Coffee

Introducción

Si te dedicas al mundo del desarrollo es posible que estes cansado de escuchar está palabra, ¿pero por qué esta tecnología está en boca de todos? ¿qué la hace tan atractiva? En este post partimos de cero y te vamos a contar en que consiste Kubernetes.

¿Qué tengo que saber?

Para poder seguir este post, tienes que tener conceptos básicos de tecnología de contenedores (Docker), si no estas familiarizados con esto puedes leer nuestro post introductorio Hola Docker

Pongamonos en contexto…

Todo comienza con un contenedor. La manera más sencilla de entender un contenedor, es que se trata de software que corre de manera independiente. Por ejemplo, digamos que quiero probar Mongo. Tradicionalmente, lo que haría, es irme a la página oficial, descargar los binarios, instalarlos, comprobar que mi instalación ha sido correcta; si no ha sido correcta, aplicar las modificaciones necesarias dependiendo de mi configuración y sistema operativo, volver a comprobar que el software corre como esperamos; si no vuelta a empezar…

Flujo tradicional instalación MongoDB
Flujo tradicional instalación MongoDB

Si por el contrario utilizo un contenedor, los pasos a seguir se simplifican, imaginemos que queremos instalar el motor de base de datos de mongo, …

Lee el post completo aquí …

Happy coding!

Greetings

El Bruno



¿Con ganas de ponerte al día?

En Lemoncode te ofrecemos formación online impartida por profesionales que se baten el cobre en consultoría:

#Windows10 – How to solve “#docker daemon is not running”. Extreme solutions like restart windows won’t work! 🙀🙀🙀

Buy Me A Coffee

Hi !

Time to share a weird experience on Windows 10 and Docker. Sometimes, usually after some Windows 10 update or even after a software installation, docker stop responding.

An typical error may look like this.

error during connect: This error may indicate that the docker daemon is not running.: Post http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.24/build?buildargs=%7B%7D&cachefrom=%5B%5D&cgroupparent=&cpuperiod=0&cpuquota=0&cpusetcpus=&cpusetmems=&cpushares=0&dockerfile=Dockerfile.amd64&labels=%7B%7D&memory=0&memswap=0&networkmode=default&rm=1&shmsize=0&t=noToday.azurecr.io%2FU%3A0.0.88-amd64&target=&ulimits=null&version=1: open //./pipe/docker_engine: The system cannot find the file specified.

From Visual Studio Code we get:

And it’s very weird. When I check the Docker desktop app, it’s stuck in the the STARTING state.

docker windows 10 for ever in starting mode

I can restart the docker desktop app, and I will still have the issue. As I said, weird.

I’m a handy man, so I decided to restart the docker service. Just 2 commands:

Net stop com.docker.service
Net start com.docker.service

However, this does not solve the problem. And sometimes, even restarting Windows won’t fix the problem.

After some time, I found the root cause:

Somehow WSL was set to version 1 instead of version 2.

I’m not sure why, however the solution is super easy. Just run a command to set WSL to version 2 and then restart docker service.

wsl --set-default-version 2
Net stop com.docker.service
Net start com.docker.service

If you are a visual person, this may look like this:

docker windows 10 set WSL2 as current version

Important: you need to run these commands with Administrator privileges. So in a Windows Terminal world, this may also look like this. Right click on the Windows Terminal App, and click on “Run as administrator”.

Happy coding!

Greetings

El Bruno



¿Con ganas de ponerte al día?

En Lemoncode te ofrecemos formación online impartida por profesionales que se baten el cobre en consultoría:

#RaspberryPi – Build #docker 🐳 images from Visual Studio @Code remotely using a Raspberry Pi. #AzureIoT

Buy Me A Coffee

Hi !

The post title is very descriptive, however let me share some more context on why I needed this scenario.

I’m currently working in an Azure IoT custom module. I got installed the Azure IoT extensions on Visual Studio Code, so everything works great on the Hello World mode. Once I wrote my code logic in C#, I can easily build my IoT Edge Solution.

My default configuration focus to an amd64 platform. I got this defined in my settings.json

{
    "azure-iot-edge.defaultPlatform": {
        "platform": "amd64",
        "alias": null
    }
}

This is a sample log output, using my local docker in Windows 10. Everything works fine.

[+] Building 0.7s (16/16) FINISHED
 => [internal] load build definition from Dockerfile.amd64                                                                                                                                                                                  0.1s
 => => transferring dockerfile: 38B                                                                                                                                                                                                         0.1s 
 => [internal] load .dockerignore                                                                                                                                                                                                           0.1s 
 => => transferring context: 34B                                                                                                                                                                                                            0.1s 
 => [internal] load metadata for mcr.microsoft.com/dotnet/core/runtime:3.1-buster-slim                                                                                                                                                      0.5s
 => [internal] load metadata for mcr.microsoft.com/dotnet/core/sdk:3.1-buster                                                                                                                                                               0.5s 
 => [build-env 1/6] FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster@sha256:0fece15a102530aa2dad9d247bc0d05db6790917696377fc56a8465604ef1aff                                                                                               0.0s
 => [internal] load build context                                                                                                                                                                                                           0.0s 
 => => transferring context: 446B                                                                                                                                                                                                           0.0s 
 => [stage-1 1/4] FROM mcr.microsoft.com/dotnet/core/runtime:3.1-buster-slim@sha256:e84300e54209cad66611b0b483cfe8c007d3bb5450388ff58e7f15d025917817                                                                                        0.0s 
 => CACHED [stage-1 2/4] WORKDIR /app                                                                                                                                                                                                       0.0s
 => CACHED [build-env 2/6] WORKDIR /app                                                                                                                                                                                                     0.0s 
 => CACHED [build-env 3/6] COPY *.csproj ./                                                                                                                                                                                                 0.0s 
 => CACHED [build-env 4/6] RUN dotnet restore                                                                                                                                                                                               0.0s 
 => CACHED [build-env 5/6] COPY . ./                                                                                                                                                                                                        0.0s 
 => CACHED [build-env 6/6] RUN dotnet publish -c Release -o out                                                                                                                                                                             0.0s 
 => CACHED [stage-1 3/4] COPY --from=build-env /app/out ./                                                                                                                                                                                  0.0s 
 => CACHED [stage-1 4/4] RUN useradd -ms /bin/bash moduleuser                                                                                                                                                                               0.0s 
 => exporting to image                                                                                                                                                                                                                      0.0s 
 => => exporting layers                                                                                                                                                                                                                     0.0s 
 => => writing image sha256:8d38fb01640fb566e761231c1a646df35bb83f24dfbb0fdea92bc4db62576e12                                                                                                                                                0.0s 
 => => naming to <...>

If I want to change the target platform to build an arm32 image, I change my settings.json file to:

{
    "azure-iot-edge.defaultPlatform": {
        "platform": "arm32v7",
        "alias": null
    }
}

However I got this ugly error, trying to build this

=> CACHED [build-env 2/6] WORKDIR /app                                                                                                                                                                                                     0.0s 
 => CACHED [build-env 3/6] COPY *.csproj ./                                                                                                                                                                                                 0.0s 
 => ERROR [build-env 4/6] RUN dotnet restore                                                                                                                                                                                                0.5s
------
 > [build-env 4/6] RUN dotnet restore:
#10 0.450 A fatal error occurred, the folder [/usr/share/dotnet/host/fxr] does not contain any version-numbered child folders
------
executor failed running [/bin/sh -c dotnet restore]: exit code: 131

There seems to be an error here, outside of my docker knowledge.

Of course, a test path could involve the use of a remote ARM32 Docker Environment to build my Azure IoT Edge C# Module. I followed my previous posts steps to enable SSH password-less access to my Raspberry Pi and also manage docker as non-root users (see references).

Now I can define a specific DOCKER EXECUTOR to build my images. I changed my settings.json file to define this executor. Note that you must define a standard SSH connection, this may involve your device name or IP Address.

{
    "azure-iot-edge.defaultPlatform": {
        "platform": "arm32v7",
        "alias": null
    },
    "azure-iot-edge.executor.env": {
         "DOCKER_HOST": "ssh://pi@<Raspberry PI Address>"
    }
}

Important: once you changed your settings.json configuration, you need to reload your VSCode environment. You can close and open the IDE, or even better, invoke the [Reload Window] command.

visual studio code reload window

Now we can sucesfully build the Azure IoT module. the output log will be similar to this one:

Sending build context to Docker daemon  24.06kB
Step 1/12 : FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster-arm32v7 AS build-env
 ---> 3cdfdcca7cd6
Step 2/12 : WORKDIR /app
 ---> Using cache
 ---> 8c3252bfdefb
Step 3/12 : COPY *.csproj ./
 ---> Using cache
 ---> e6fe11cb659e
Step 4/12 : RUN dotnet restore
 ---> Using cache
 ---> 12453c83de65
Step 5/12 : COPY . ./
 ---> Using cache
 ---> 107c1dfccfa3
Step 6/12 : RUN dotnet publish -c Release -o out
 ---> Using cache
 ---> 603a93c80b0e
Step 7/12 : FROM mcr.microsoft.com/dotnet/core/runtime:3.1-buster-slim-arm32v7
 ---> 2bb8fc093aad
Step 8/12 : WORKDIR /app
 ---> Using cache
 ---> c8a93a2a7b90
Step 9/12 : COPY --from=build-env /app/out ./
 ---> Using cache
 ---> f72a492270ad
Step 10/12 : RUN useradd -ms /bin/bash moduleuser
 ---> Using cache
 ---> 082a4568867b
Step 11/12 : USER moduleuser
 ---> Using cache
 ---> 2c5e7b562289
Step 12/12 : ENTRYPOINT ["dotnet", "UDPFwder.dll"]
 ---> Using cache
 ---> f523123332f9
Successfully built f523123332f9
Successfully tagged <...>
SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.

And that’s it! Using a Raspberry Pi device to build Net Azure IoT modules seems to be the fix to build ARM32 platforms.

References

Happy coding!

Greetings

El Bruno


My posts on Raspberry Pi ⚡🐲⚡

Dev posts for Raspberry Pi
Tools and Apps for Raspberry Pi
Setup the device
Hardware


¿Con ganas de ponerte al día?

En Lemoncode te ofrecemos formación online impartida por profesionales que se baten el cobre en consultoría:

#RaspberryPi – Manage #docker 🐳 as a non-root user

Buy Me A Coffee

Hi !

The next step for my [use Docker in a Raspberry Pi to build and deploy images] step is to configure docker as non-root user.

Lucky us, this is officially documented and easy.

Create the docker group.

sudo groupadd docker

Add your user to the docker group.

sudo usermod -aG docker $USER

Log out and log back in so that your group membership is re-evaluated.

Run a test command for docker without sudo, in example let’s see the container list

docker container ls

And that’s it!

References

Happy coding!

Greetings

El Bruno


My posts on Raspberry Pi ⚡🐲⚡

Dev posts for Raspberry Pi
Tools and Apps for Raspberry Pi
Setup the device
Hardware


¿Con ganas de ponerte al día?

En Lemoncode te ofrecemos formación online impartida por profesionales que se baten el cobre en consultoría:

#RaspberryPi – Setup SSH passwordless access to remote work with #docker 🐳

Buy Me A Coffee

Hi !

Due to some weird configurations, I’m having issues building some .NET 5 docker images locally in my Windows 10 machine. So I decided to use a Raspberry 4 as build server, and 1st step is to setup the SSH access without the need of username / password. The main resource is an official one fro the Raspberry Pi org: Passwordless SSH access.

Once we finished these steps, we can easyly configure a TCP-enabled Docker.

I followed the next steps. First, let’s edit docker.service with the command:

sudo nano /lib/systemd/system/docker.service 

And add [-H tcp://0.0.0.0:2375] at [ExecStart] section

And restart the device or just the needed services

  • run sudo systemctl daemon-reload
  • run sudo systemctl restart docker

In next posts, how to add the needed configuration to Visual Studio Code to build the images remotely in the Raspberry Pi.

Happy coding!

Greetings

El Bruno


My posts on Raspberry Pi ⚡🐲⚡

Dev posts for Raspberry Pi
Tools and Apps for Raspberry Pi
Setup the device
Hardware


¿Con ganas de ponerte al día?

En Lemoncode te ofrecemos formación online impartida por profesionales que se baten el cobre en consultoría:

#Azure – How to push / export and run a docker python flask web app on Azure

Buy Me A Coffee

Hi !

Here is an important reminder before starting:

Please do not use as a guide to deploy to production environments.

I was reading a lot about Azure Container Registry and other container scenarios. And most of the demos / tutorials are based on .Net Container Web Apps. Which is great, however there are no much information about the same process using Python Container Web Apps. So, I take this as an excuse to learn something new and here I am 😁, Let’s start.

Python Web App using Flask

I started with a simple Python Flask app. For this demo, I will use a post I write about flask and multi threading (see references for the full post).

# Bruno Capuano
# start a webserver with flask in a thread
# start a different thread +1 a shared var

from flask import Flask                                                         
import threading
import time

iCounter = 0
data = 'foo'
app = Flask(__name__)

def mainSum():
    # increment counter every second
    global iCounter
    while True:
        iCounter = iCounter + 1
        t = time.localtime()
        current_time = time.strftime("%H:%M:%S", t)    
        print(str(f"{current_time} - data {iCounter}"))
        time.sleep(1)

def startWebServer():
     app.run(host='0.0.0.0', port=8100)

@app.route("/getdata")
def main():
    global iCounter
    t = time.localtime()
    current_time = time.strftime("%H:%M:%S", t)    
    return str(f"{current_time} - data {iCounter}")

if __name__ == "__main__":
    stateThread = threading.Thread(target=mainSum)
    stateThread.daemon = True
    stateThread.start()

    webThread = threading.Thread(target=startWebServer)
    webThread.start()

This a resource consuming app, so be careful with this code!

Create a Docker container for the app

So next step was a to create a container for the app. This is my docker file. I added the commands to build and run the image at the bottom.

FROM python:3.7-slim

RUN pip install -U pip
RUN pip install --no-cache-dir flask~=1.1.2

COPY app /app

# Expose the port, default 8100
EXPOSE 8100

# Set the working directory
WORKDIR /app

# Run the flask server for the endpoints
CMD python -u app.py

# Commands to build and run the image
# docker build --pull --rm -f "Dockerfile" -t dockertoazure-webapp-service "."
# docker run -d -p 8100:8100 --privileged --name dockertoazure-webapp-service dockertoazure-webapp-service:latest

So, build and run my docker image and everything works fine !

Double check in Portainer, because I’m not a command line person

docker to azure build and run local ok in portainer

So, here my 1st milestone:

A local python web app using flask, in a docker container ready to be used elsewhere.

Push local Image to Azure

Our next step will be to have a Azure container registry, that will be the place that we will use as repository in Azure to host our images. In the references section the official definition is:

An Azure container registry is a private Docker registry in Azure where you can store and manage private Docker container images and related artifacts. In this quickstart, you create a container registry with the Azure portal. Then, use Docker commands to push a container image into the registry, and finally pull and run the image from your registry.

Quickstart: Create an Azure container registry using the Azure portal

I will assume Azure CLI is installed, so next step will be to login to azure and also login to your container.

# LOGIN TO AZURE
az login
az acr login --name <container name>.azurecr.io

Next step will require to have a tagged image for the Azure Container Registry.

# TAG LOCAL IMAGE
docker tag dockertoazure-webapp-service <container name>.azurecr.io/dockertoazure-webapp-service:v1

And, time to push the local image to the cloud !

# PUSH LOCAL IMAGE
docker push <container name>.azurecr.io/dockertoazure-webapp-service:v1

And we have 2 ways to check if the image was successfully pushed. Using the command line

# LIST CURRENT REPOSITORIES
az acr repository list -n <container name>.azurecr.io
docker to azure list azure container registry repositories

We can also use the Azure Portal to check the available repositories

docker to azure azure container repositories list

Running the Docker image in a WebApp

And here comes the tricky part. Once you select a repository, you can see the tags of the image and you can deploy a WebApp directly from there.

docker to azure docker image available options

However, if you haven’t created a WebApp using other steps, you won’t have this option enabled.

Note: My guess is that until you have an Linux usage plan created, the [Run instance] and [Deploy to web app] options will be disabled. In this scenario, you may want to skip next steps and go directly to option 2.

Let’s see both options. From the context menu, from the previous image, create a web app is just a single step

docker to azure create using the context menu

A couple of seconds later we have our App up and running

The 2nd option to create your Web App is creating a new [Web App for Containers]

And here we found a couple of interesting options, like create a web site based on a docker container:

And in the next step, we can choose the image that we uploaded in previous steps

new web app docker image details

And, again, a couple of seconds later, we also have this new App up and running

Conclusion

So, nothing new here. I haven’t found an article or post, that describes this, so I put all the pieces together and dump this lines. Again an important warning

There are much better ways to do this ! Think on a DevOps approach.

I promise to write a follow up on this one, with the step-by-step details on this using GitHub actions. Which is the right way.

Happy coding!

Greetings

El Bruno


Resources

#docker – Updating docker containers automatically with Watchtower. Bonus: let’s learn Go !

Buy Me A Coffee

Hi !

A couple of days ago, I wrote about how I like Portainer as a visual interface to manage docker (see references). In one of the comments, Marco suggested also an option to automatically update all my containers.

As I explained in my post, the basic steps for this are

  • docker stop
  • docker rm
  • docker pull
  • docker run

Super easy, but tedious if you need to do this several times. Let’s meet Watchover.

Watchtover has the ability to “watch” running Docker containers on either the same local or remote host, check if there is a newer image in the remote registry, and then update the container with the new image using the same configuration options it was instantiated with.

I give this a try, and I found that it was very easy, just a command

docker run -d \
    --name watchtower \
    -v /var/run/docker.sock:/var/run/docker.sock \
    containrrr/watchtower

And, there is a personal bonus here. I’ve been looking for an excuse to start to play around with Go, and this is the one. I think, I’ll use the excuse of update my containers as my next phase on Go learning.

Happy coding!

Greetings

El Bruno


References

#docker – How to update Portainer (big fan here!)

Buy Me A Coffee

Hi !

I’m a big fan of Portanier. I usually will go for a graphical interface on top of a command line option, and to manage my docker labs, Portainer is my best choice for this.

Portainer is a lightweight management UI which allows you to easily manage your different Docker environments (Docker hosts or Swarm clusters). Portainer is meant to be as simple to deploy as it is to use. It consists of a single container that can run on any Docker engine (can be deployed as Linux container or a Windows native container, supports other platforms too). Portainer allows you to manage all your Docker resources (containers, images, volumes, networks and more) ! It is compatible with the standalone Docker engine and with Docker Swarm mode.

Portainer (see references)

From time to time, we have updates for Portainer and I always make a mess doing this manually. So here is my personal cheat sheet for the next one

  • stop Portainer container
  • remove Portainer container
  • get latest Portainer image
  • run Portainer

And the docker commands are

docker stop Portainer
docker rm Portainer
docker pull portainer/portainer
docker run -d -p 8000:8000 -p 9000:9000 --name="Portainer" -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer

Portainer is updated and now I got just news in the home page !

portainer home page

Happy coding!

Greetings

El Bruno


Resources

#Coding4Fun – How to control your #drone with 20 lines of code! (17/N)

Buy Me A Coffee

Hi !

Once we have the a custom vision trained model instance, we can use it to recognize objects from the drone camera feed. Read my previous posts for descriptions on these.

Another interesting scenario, is to save local files for every detected object. In the following code, I’ll save 2 different files for every detected object

  • A camera frame image, with a frame around the detected object
  • A plain text file with the JSON information

In the sample code below, the save process is in the lines 122-129. And, not in a fancy way, the files have the same name to correlate them.

drone recognized files

So let’s go to the full code:

# Bruno Capuano
# open camera with openCV
# analyze camera frame with local docker custom vision project
# draw bounding boxes for each reconized object
import socket
import time
import threading
import cv2
import urllib
import json
import requests
import os
from flask import Flask, request, jsonify
def receiveData():
global response
while True:
try:
response, _ = clientSocket.recvfrom(1024)
except:
break
def readStates():
global battery
while True:
try:
response_state, _ = stateSocket.recvfrom(256)
if response_state != 'ok':
response_state = response_state.decode('ASCII')
list = response_state.replace(';', ':').split(':')
battery = int(list[21])
pitch = int(list[1])
except:
break
def sendCommand(command):
global response
timestamp = int(time.time() * 1000)
clientSocket.sendto(command.encode('utf-8'), address)
while response is None:
if (time.time() * 1000) timestamp > 5 * 1000:
return False
return response
def sendReadCommand(command):
response = sendCommand(command)
try:
response = str(response)
except:
pass
return response
def sendControlCommand(command):
response = None
for i in range(0, 5):
response = sendCommand(command)
if response == 'OK' or response == 'ok':
return True
return False
# ———————————————–
# Local calls
# ———————————————–
probabilityThreshold = 75
def displayPredictions(jsonPrediction, frame, frameImageFileName):
global camera_Width, camera_Heigth
jsonObj = json.loads(jsonPrediction)
preds = jsonObj['predictions']
sorted_preds = sorted(preds, key=lambda x: x['probability'], reverse=True)
strSortedPreds = ""
resultFound = False
if (sorted_preds):
# open img to save results
img = cv2.imread(frameImageFileName)
detected = False
for pred in sorted_preds:
# tag name and prob * 100
tagName = str(pred['tagName'])
probability = pred['probability'] * 100
# apply threshold
if (probability >= probabilityThreshold):
detected = True
bb = pred['boundingBox']
resize_factor = 100
height = int(bb['height'] * resize_factor)
left = int(bb['left'] * resize_factor)
top = int(bb['top'] * resize_factor)
width = int(bb['width'] * resize_factor)
print(f'height = {height} – left {left} – top {top} – width {width}')
# adjust to size
camera_Width,
height = int(height * camera_Heigth / 100)
left = int(left * camera_Width / 100)
top = int(top * camera_Heigth / 100)
width = int(width * camera_Width / 100)
print(f'Adjusted height = {height} – left {left} – top {top} – width {width}')
# draw bounding boxes
start_point = (top, left)
end_point = (top + height, left + width)
print(f'MVP – {probability}')
print(f'start point: {start_point} – end point: {end_point}')
color = (255, 0, 0)
thickness = 2
cv2.rectangle(img, start_point, end_point, color, thickness)
print(jsonPrediction)
# save the detected image
cv2.rectangle(img, start_point, end_point, color, thickness)
if (detected == True):
detImageFileName = frameImageFileName.replace('tmp', 'det')
cv2.imwrite(detImageFileName, img)
detJsonFileName = detImageFileName.replace('png', 'json')
save_text = open(detJsonFileName, 'w')
save_text.write(jsonStr)
save_text.close()
return strSortedPreds
# instantiate flask app and push a context
app = Flask(__name__)
# ———————————————–
# Main program
# ———————————————–
# connection info
UDP_IP = '192.168.10.1'
UDP_PORT = 8889
last_received_command = time.time()
STATE_UDP_PORT = 8890
address = (UDP_IP, UDP_PORT)
response = None
response_state = None
clientSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
clientSocket.bind(('', UDP_PORT))
stateSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
stateSocket.bind(('', STATE_UDP_PORT))
# start threads
recThread = threading.Thread(target=receiveData)
recThread.daemon = True
recThread.start()
stateThread = threading.Thread(target=readStates)
stateThread.daemon = True
stateThread.start()
# connect to drone
response = sendControlCommand("command")
print(f'command response: {response}')
response = sendControlCommand("streamon")
print(f'streamon response: {response}')
# drone information
battery = 0
pitch = 0
# open UDP
print(f'opening UDP video feed, wait 2 seconds ')
videoUDP = 'udp://192.168.10.1:11111'
cap = cv2.VideoCapture(videoUDP)
time.sleep(2)
camera_Width = 640
camera_Heigth = 480
# open
i = 0
while True:
i = i + 1
imgNumber = str(i).zfill(5)
start_time = time.time()
sendReadCommand('battery?')
print(f'battery: {battery} % – pitch: {pitch} – i: {imgNumber}')
try:
ret, frame = cap.read()
img = cv2.resize(frame, (camera_Width, camera_Heigth))
# save image to disk and open it
frameImageFileName = str(f'tmp\image{imgNumber}.png')
cv2.imwrite(frameImageFileName, img)
with open(frameImageFileName, 'rb') as f:
img_data = f.read()
# analyze file in local container
api_url = "http://127.0.0.1:8070/image&quot;
r = requests.post(api_url, data=img_data)
with app.app_context():
jsonResults = jsonify(r.json())
jsonStr = jsonResults.get_data(as_text=True)
displayPredictions(jsonStr, frame, frameImageFileName)
fpsInfo = ""
if (time.time() start_time ) > 0:
fpsInfo = "FPS: " + str(1.0 / (time.time() start_time)) # FPS = 1 / time to process loop
font = cv2.FONT_HERSHEY_DUPLEX
cv2.putText(img, fpsInfo, (10, 20), font, 0.4, (255, 255, 255), 1)
cv2.imshow('@elbruno – DJI Tello Camera', img)
except Exception as e:
print(f'exc: {e}')
pass
if cv2.waitKey(1) & 0xFF == ord('q'):
break
response = sendControlCommand("streamoff")
print(f'streamon response: {response}')

And if you want to see this up and running, it’s much better to see this in a video (start at ):

The complete source code can be found here https://github.com/elbruno/events/tree/master/2020%2004%2018%20Global%20AI%20On%20Tour%20MTY%20Drone%20AI%20Mex

Happy coding!

Greetings

El Bruno


References

#Coding4Fun – How to control your #drone with 20 lines of code! (16/N)

Buy Me A Coffee

Hi !

In my previous post, I shared an example where I analyzed the camera feed using a Image Recognition model created using Custom Vision. Today I’ll expand the sample, and show in real time the detected MVPs logos with a frame in the drone camera feed.

Let’s take a look at the demo working in the following image.

drone camera image analysis using custom vision and drawing frames for detected objects

In the top of the image, we can see the app console log, with the information received for each analyzed frame. When an image is detected, we can see the tag, the probability and the bounding box coordinates.

A sample JSON return string start like this one:

{
  "created": "2020-04-08T17:22:02.179359",
  "id": "",
  "iteration": "",
  "predictions": [
    {
      "boundingBox": {
        "height": 0.1979116,
        "left": 0.3235259,
        "top": 0.05847502,
        "width": 0.20438321
      },
      "probability": 0.89171505,
      "tagId": 0,
      "tagName": "MVP"
    },
    {
      "boundingBox": {
        "height": 0.2091526,
        "left": 0.65271178,
        "top": 0.0433814,
        "width": 0.17669522
      },
      "probability": 0.70330358,
      "tagId": 0,
      "tagName": "MVP"
    },

In order to position the frames in the correct location, I need to make some math using the current camera and image size and the returned bounding box values for, height, left, top and width. Lines 87-110.

resize_factor = 100

height = int(bb['height'] * resize_factor)
left = int(bb['left'] * resize_factor)
top = int(bb['top'] * resize_factor)
width = int(bb['width'] * resize_factor)

# adjust to size
camera_Width, 
height = int(height * camera_Heigth / 100)
left = int(left * camera_Width / 100)
top = int(top * camera_Heigth / 100)
width = int(width * camera_Width / 100)

# draw bounding boxes
start_point = (top, left)                 
end_point = (top + height, left + width) 
color = (255, 0, 0) 
thickness = 2                
cv2.rectangle(img, start_point, end_point, color, thickness)            

So let’s go to the full code:

# Bruno Capuano
# open camera with openCV
# analyze camera frame with local docker custom vision project
# draw bounding boxes for each reconized object
import socket
import time
import threading
import cv2
import urllib
import json
import requests
import os
from flask import Flask, request, jsonify
def receiveData():
global response
while True:
try:
response, _ = clientSocket.recvfrom(1024)
except:
break
def readStates():
global battery
while True:
try:
response_state, _ = stateSocket.recvfrom(256)
if response_state != 'ok':
response_state = response_state.decode('ASCII')
list = response_state.replace(';', ':').split(':')
battery = int(list[21])
pitch = int(list[1])
except:
break
def sendCommand(command):
global response
timestamp = int(time.time() * 1000)
clientSocket.sendto(command.encode('utf-8'), address)
while response is None:
if (time.time() * 1000) timestamp > 5 * 1000:
return False
return response
def sendReadCommand(command):
response = sendCommand(command)
try:
response = str(response)
except:
pass
return response
def sendControlCommand(command):
response = None
for i in range(0, 5):
response = sendCommand(command)
if response == 'OK' or response == 'ok':
return True
return False
# ———————————————–
# Local calls
# ———————————————–
probabilityThreshold = 75
def displayPredictions(jsonPrediction, frame):
global camera_Width, camera_Heigth
jsonObj = json.loads(jsonPrediction)
preds = jsonObj['predictions']
sorted_preds = sorted(preds, key=lambda x: x['probability'], reverse=True)
strSortedPreds = ""
resultFound = False
if (sorted_preds):
for pred in sorted_preds:
# tag name and prob * 100
tagName = str(pred['tagName'])
probability = pred['probability'] * 100
# apply threshold
if (probability >= probabilityThreshold):
bb = pred['boundingBox']
resize_factor = 100
height = int(bb['height'] * resize_factor)
left = int(bb['left'] * resize_factor)
top = int(bb['top'] * resize_factor)
width = int(bb['width'] * resize_factor)
#print(f'height = {height} – left {left} – top {top} – width {width}')
# adjust to size
camera_Width,
height = int(height * camera_Heigth / 100)
left = int(left * camera_Width / 100)
top = int(top * camera_Heigth / 100)
width = int(width * camera_Width / 100)
#print(f'Adjusted height = {height} – left {left} – top {top} – width {width}')
# draw bounding boxes
start_point = (top, left)
end_point = (top + height, left + width)
color = (255, 0, 0)
thickness = 2
cv2.rectangle(img, start_point, end_point, color, thickness)
print(f'MVP – {probability}')
print(f'start point: {start_point} – end point: {end_point}')
print(jsonPrediction)
return strSortedPreds
# instantiate flask app and push a context
app = Flask(__name__)
# ———————————————–
# Main program
# ———————————————–
# connection info
UDP_IP = '192.168.10.1'
UDP_PORT = 8889
last_received_command = time.time()
STATE_UDP_PORT = 8890
address = (UDP_IP, UDP_PORT)
response = None
response_state = None
clientSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
clientSocket.bind(('', UDP_PORT))
stateSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
stateSocket.bind(('', STATE_UDP_PORT))
# start threads
recThread = threading.Thread(target=receiveData)
recThread.daemon = True
recThread.start()
stateThread = threading.Thread(target=readStates)
stateThread.daemon = True
stateThread.start()
# connect to drone
response = sendControlCommand("command")
print(f'command response: {response}')
response = sendControlCommand("streamon")
print(f'streamon response: {response}')
# drone information
battery = 0
pitch = 0
# open UDP
print(f'opening UDP video feed, wait 2 seconds ')
videoUDP = 'udp://192.168.10.1:11111'
cap = cv2.VideoCapture(videoUDP)
time.sleep(2)
camera_Width = 640
camera_Heigth = 480
# open
i = 0
while True:
i = i + 1
imgNumber = str(i).zfill(5)
start_time = time.time()
sendReadCommand('battery?')
print(f'battery: {battery} % – pitch: {pitch} – i: {imgNumber}')
try:
ret, frame = cap.read()
img = cv2.resize(frame, (camera_Width, camera_Heigth))
# save image to disk and open it
frameImageFileName = str(f'tmp\image{imgNumber}.png')
cv2.imwrite(frameImageFileName, img)
with open(frameImageFileName, 'rb') as f:
img_data = f.read()
# analyze file in local container
api_url = "http://127.0.0.1:8070/image&quot;
r = requests.post(api_url, data=img_data)
with app.app_context():
jsonResults = jsonify(r.json())
jsonStr = jsonResults.get_data(as_text=True)
displayPredictions(jsonStr, frame)
fpsInfo = ""
if (time.time() start_time ) > 0:
fpsInfo = "FPS: " + str(1.0 / (time.time() start_time)) # FPS = 1 / time to process loop
font = cv2.FONT_HERSHEY_DUPLEX
cv2.putText(img, fpsInfo, (10, 20), font, 0.4, (255, 255, 255), 1)
cv2.imshow('@elbruno – DJI Tello Camera', img)
except Exception as e:
print(f'exc: {e}')
pass
if cv2.waitKey(1) & 0xFF == ord('q'):
break
response = sendControlCommand("streamoff")
print(f'streamon response: {response}')

And if you want to see this up and running, it’s much better to see this in a video (start at ):

The complete source code can be found here https://github.com/elbruno/events/tree/master/2020%2004%2018%20Global%20AI%20On%20Tour%20MTY%20Drone%20AI%20Mex

Happy coding!

Greetings

El Bruno


References