venerdì 15 marzo 2024

Canon 450D e Gphoto2

 GPhoto 2 permette il controllo remoto di alcuni modelli di DSLR. Ho provato con la mia Canon EOS 500D

 

 

Al primo tentativo  il comando gphoto2 --auto-detect
Model                          Port                                            
----------------------------------------------------------
Canon EOS 500D                 usb:001,002    

funzionava ma i comandi successivi risultano con l'errore 

An error occurred in the io-library ('Could not claim the USB device'): Could not claim interface 0 (Device or resource busy). Make sure no other program (gvfs-gphoto2-volume-monitor) or kernel module (such as sdc2xx, stv680, spca50x) is using the device and you have read/write access to the device.
ERROR: Could not capture image.

Spenta e riaccesa la camera ho potuto eseguire il list dei files sulla SD

gphoto2 --list-files

Effettuare l'upload di una immagine 

gphoto2 --get-file /store_00020001/DCIM/100CANON/IMG_9200.JPG

effettuare scatti

gphoto2 --capture-image
gphoto2 --capture-image-and-download --filename %m%d%H%M%S.jpg

Con questo comando si ottengono i raw CRE

gphoto2 --get-all-raw-data

 Per convertire in raw in PNG si puo' usare Darktable in modalita' terminale

for pic in *.cr2; do darktable-cli "$pic" "$(basename ${pic%.CR2}.png)";  done

Oppure imagemagick

mogrify -format png *.cr2

Interessante e' anche la libreria Python

https://github.com/jim-easterbrook/python-gphoto2

giovedì 14 marzo 2024

Pyrealsense

ATTENZIONE: per funzionare alla massima risoluzione la Realsense deve usare una porta USB 3 ed un cavo idoneo ad USB 3 altrimenti si limita a 640x480

 

==================================================

Solo Ottico 1920x1080

import pyrealsense2 as rs
import numpy as np
import cv2
import time
import math

pipeline = rs.pipeline()
config = rs.config()

config.enable_stream(rs.stream.color, 1920, 1080, rs.format.bgr8, 30)

profile = pipeline.start(config)

align_to = rs.stream.color
align = rs.align(align_to)

frames = pipeline.wait_for_frames()
aligned_frames = align.process(frames)
color_frame = aligned_frames.get_color_frame()
color_image = np.asanyarray(color_frame.get_data())
imageName1 = str(time.strftime("%Y_%m_%d_%H_%M_%S")) +  '_Color.png'
cv2.imwrite(imageName1, color_image)
pipeline.stop()


 

================================================== 

Ottico + Profondita' 1280x720 

 

================================================== 

import pyrealsense2 as rs
import numpy as np
import cv2
import time
import math

pipeline = rs.pipeline()
config = rs.config()

config.enable_stream(rs.stream.depth, 1280, 720, rs.format.z16, 30)
config.enable_stream(rs.stream.color, 1280, 720, rs.format.bgr8, 30)

profile = pipeline.start(config)
depth_sensor = profile.get_device().first_depth_sensor()
depth_scale = depth_sensor.get_depth_scale()

# We will be removing the background of objects more than
#  clipping_distance_in_meters meters away
clipping_distance_in_meters = 1.5
clipping_distance = clipping_distance_in_meters / depth_scale


align_to = rs.stream.color
align = rs.align(align_to)

frames = pipeline.wait_for_frames()

aligned_frames = align.process(frames)
aligned_depth_frame = aligned_frames.get_depth_frame()
color_frame = aligned_frames.get_color_frame()

depth_image = np.asanyarray(aligned_depth_frame.get_data())
color_image = np.asanyarray(color_frame.get_data())


# Remove background - Set pixels further than clipping_distance to grey
grey_color = 153
depth_image_3d = np.dstack((depth_image,depth_image,depth_image)) #depth image is 1 channel, color is 3 channels
bg_removed = np.where((depth_image_3d > clipping_distance) | (depth_image_3d <= 0), grey_color, color_image)

# Render images
depth_colormap = cv2.applyColorMap(cv2.convertScaleAbs(depth_image, alpha=0.03), cv2.COLORMAP_JET)
images = np.hstack((bg_removed, depth_colormap))
#cv2.namedWindow('Align Example', cv2.WINDOW_AUTOSIZE)

# Filename
imageName1 = str(time.strftime("%Y_%m_%d_%H_%M_%S")) +  '_Color.png'
imageName2 = str(time.strftime("%Y_%m_%d_%H_%M_%S")) +  '_Depth.png'
imageName3 = str(time.strftime("%Y_%m_%d_%H_%M_%S")) +  '_bg_removed.png'
imageName4 = str(time.strftime("%Y_%m_%d_%H_%M_%S")) +  '_ColorDepth.png'
imageName5 = str(time.strftime("%Y_%m_%d_%H_%M_%S")) +  '_DepthColormap.png'

# Saving the image
cv2.imwrite(imageName1, color_image)
cv2.imwrite(imageName2, depth_image)
cv2.imwrite(imageName3, images)
cv2.imwrite(imageName4, bg_removed )
cv2.imwrite(imageName5, depth_colormap )

pipeline.stop()



================================================== 

Infrarosso' 1280x720

================================================== 

import pyrealsense2 as rs
import numpy as np
import cv2
import time
import math
 
pipeline = rs.pipeline()
config = rs.config()

config.enable_stream(rs.stream.infrared, 1, 1280, 720, rs.format.y8, 30)
profile = pipeline.start(config)
 
frames = pipeline.wait_for_frames()
ir1_frame = frames.get_infrared_frame(1)
image = np.asanyarray(ir1_frame.get_data())
imageIR = str(time.strftime("%Y_%m_%d_%H_%M_%S")) +  '_IR.png'
cv2.imwrite(imageIR, image)
pipeline.stop()




================================================== 

Infrarosso' IR Emitter OFF 1280x720

==================================================

import pyrealsense2 as rs
import numpy as np
import cv2
import time
import math
 
pipeline = rs.pipeline()
config = rs.config()
config.enable_stream(rs.stream.infrared, 1, 1280, 720, rs.format.y8, 30)

#disabilita disable IR emitter
pipeline_profile = pipeline.start(config)
device = pipeline_profile.get_device()
depth_sensor = device.query_sensors()[0]
if depth_sensor.supports(rs.option.emitter_enabled):
    depth_sensor.set_option(rs.option.emitter_enabled, 0)

frames = pipeline.wait_for_frames()
ir1_frame = frames.get_infrared_frame(1)
image = np.asanyarray(ir1_frame.get_data())
imageIR = str(time.strftime("%Y_%m_%d_%H_%M_%S")) +  '_IR_OFF.png'
cv2.imwrite(imageIR, image)
pipeline.stop() 

 


================================================== 

IMU

==================================================

import pyrealsense2 as rs

import numpy as np


def initialize_camera():
    # start the frames pipe
    p = rs.pipeline()
    conf = rs.config()
    conf.enable_stream(rs.stream.accel)
    conf.enable_stream(rs.stream.gyro)
    prof = p.start(conf)
    return p


def gyro_data(gyro):
    return np.asarray([gyro.x, gyro.y, gyro.z])


def accel_data(accel):
    return np.asarray([accel.x, accel.y, accel.z])

p = initialize_camera()
try:
    while True:
        f = p.wait_for_frames()
        accel = accel_data(f[0].as_motion_frame().get_motion_data())
        gyro = gyro_data(f[1].as_motion_frame().get_motion_data())
        print("accelerometer: ", accel)
        print("gyro: ", gyro)

finally:
    p.stop()

 

gyro:  [0.         0.         0.00349066]
accelerometer:  [-0.24516624 -8.78675842 -2.75566864]

 

================================================== 

REGOLA ESPOSIZIONE

==================================================

import pyrealsense2 as rs
pipeline = rs.pipeline()
config = rs.config()
profile = pipeline.start(config) # Start streaming
sensor_dep = profile.get_device().first_depth_sensor()
print("Trying to set Exposure")
exp = sensor_dep.get_option(rs.option.exposure)
print ("exposure = %d" % exp)
print ("Setting exposure to new value")
exp = sensor_dep.set_option(rs.option.exposure, 25000)
exp = sensor_dep.get_option(rs.option.exposure)
print ("New exposure = %d" % exp)
profile = pipeline.stop

l'esposizione si puo' regolare anche su ROI

p = rs.pipeline()
prof = p.start()
s = prof.get_device().first_roi_sensor()
roi = s.get_region_of_interest()
s.set_region_of_interest(roi)

 

================================================== 

ADVANVCED MODE (regola parametri di dettaglio)

==================================================

 

import pyrealsense2 as rs
import time
import json

DS5_product_ids = ["0AD1", "0AD2", "0AD3", "0AD4", "0AD5", "0AF6", "0AFE", "0AFF", "0B00", "0B01", "0B03", "0B07", "0B3A", "0B5C"]

def find_device_that_supports_advanced_mode() :
    ctx = rs.context()
    ds5_dev = rs.device()
    devices = ctx.query_devices();
    for dev in devices:
        if dev.supports(rs.camera_info.product_id) and str(dev.get_info(rs.camera_info.product_id)) in DS5_product_ids:
            if dev.supports(rs.camera_info.name):
                print("Found device that supports advanced mode:", dev.get_info(rs.camera_info.name))
            return dev
    raise Exception("No D400 product line device that supports advanced mode was found")

try:
    dev = find_device_that_supports_advanced_mode()
    advnc_mode = rs.rs400_advanced_mode(dev)
    print("Advanced mode is", "enabled" if advnc_mode.is_enabled() else "disabled")

    # Loop until we successfully enable advanced mode
    while not advnc_mode.is_enabled():
        print("Trying to enable advanced mode...")
        advnc_mode.toggle_advanced_mode(True)
        # At this point the device will disconnect and re-connect.
        print("Sleeping for 5 seconds...")
        time.sleep(5)
        # The 'dev' object will become invalid and we need to initialize it again
        dev = find_device_that_supports_advanced_mode()
        advnc_mode = rs.rs400_advanced_mode(dev)
        print("Advanced mode is", "enabled" if advnc_mode.is_enabled() else "disabled")

    # Get each control's current value
    print("Depth Control: \n", advnc_mode.get_depth_control())
    print("RSM: \n", advnc_mode.get_rsm())
    print("RAU Support Vector Control: \n", advnc_mode.get_rau_support_vector_control())
    print("Color Control: \n", advnc_mode.get_color_control())
    print("RAU Thresholds Control: \n", advnc_mode.get_rau_thresholds_control())
    print("SLO Color Thresholds Control: \n", advnc_mode.get_slo_color_thresholds_control())
    print("SLO Penalty Control: \n", advnc_mode.get_slo_penalty_control())
    print("HDAD: \n", advnc_mode.get_hdad())
    print("Color Correction: \n", advnc_mode.get_color_correction())
    print("Depth Table: \n", advnc_mode.get_depth_table())
    print("Auto Exposure Control: \n", advnc_mode.get_ae_control())
    print("Census: \n", advnc_mode.get_census())

    #To get the minimum and maximum value of each control use the mode value:
    query_min_values_mode = 1
    query_max_values_mode = 2
    current_std_depth_control_group = advnc_mode.get_depth_control()
    min_std_depth_control_group = advnc_mode.get_depth_control(query_min_values_mode)
    max_std_depth_control_group = advnc_mode.get_depth_control(query_max_values_mode)
    print("Depth Control Min Values: \n ", min_std_depth_control_group)
    print("Depth Control Max Values: \n ", max_std_depth_control_group)

    # Set some control with a new (median) value
    current_std_depth_control_group.scoreThreshA = int((max_std_depth_control_group.scoreThreshA - min_std_depth_control_group.scoreThreshA) / 2)
    advnc_mode.set_depth_control(current_std_depth_control_group)
    print("After Setting new value, Depth Control: \n", advnc_mode.get_depth_control())

    # Serialize all controls to a Json string
    serialized_string = advnc_mode.serialize_json()
    print("Controls as JSON: \n", serialized_string)
    as_json_object = json.loads(serialized_string)

    # We can also load controls from a json string
    # For Python 2, the values in 'as_json_object' dict need to be converted from unicode object to utf-8
    if type(next(iter(as_json_object))) != str:
        as_json_object = {k.encode('utf-8'): v.encode("utf-8") for k, v in as_json_object.items()}
    # The C++ JSON parser requires double-quotes for the json object so we need
    # to replace the single quote of the pythonic json to double-quotes
    json_string = str(as_json_object).replace("'", '\"')
    advnc_mode.load_json(json_string)

except Exception as e:
    print(e)
    pass

Da VirtualBox a PC

L'idea di questa prova era quella di configurare un thin client senza connetterlo a tastiera, monitor e mouse

Ho avuto in prova un Praim C33 ed ho deciso di usarlo come macchina test

 


 La macchina monta 2 Gb di Ram ed un disco mSATA da 8 G quindi la distro deve essere molto leggera...mi sono orientato su Alpine Linux



Mi sono creato su Virtualbox una macchina virtuale con Alpine e Docker (circa 500 Mb di spazio disco occupato) ed ho configurato utenti, rete ed accessori vari

Poi con il comando il file vdi e' stato convertito in img


VBoxManage internalcommands converttoraw Alpine.vdi Alpine.img

L'immagine e' stata quindi flashata sull' mSata tramite un adattatore USB-mSata e flashata con BalenaEtcher oppure tramite dd

dd if=Alpine.img of=/dev/sdb bs=1k conv=sync,noerror status=progress


(attenzione il disco e' dichiarato da 8G ma poi e' risultato essere piu' piccolo quindi l'immagine di Virtualbox doveva essere inferiore a 8G)

A questo punto ho rimontato il thin client ed e' partito regolarmente con l'immagine creata su Virtualbox....attenzione il Bios deve essere impostato come Legacy oppure Legacy+UEFI non solo UEFI

 

lunedì 11 marzo 2024

Zima Blade

Mi sono preso una Zima Blade attirato piu' che altro dal basso consumo e di avere la possibilita' di avere uscita SATA per dischi esterni

Sul dispositivo e' preinstallato CasaOS ma di fatto e' una Debian 11 con il solo utente root e password casaos

 


Inserire la ram non e' banale perche' bisogna un po' forzare sulla cornice esterna in plastica.
Una grossa limitazione e' l'unica porta USB (almeno e' USB3) e l'uscita miniDP (che di fatto si usa solo per la prima configurazione ma oggi come oggi era piu' comoda una HDMI)

 Per rimuovere all'avvio CasaOs e' sufficiente disabilitare i seguenti servizi

casaos-app-management.service
casaos-gateway.service
casaos-local-storage.service
casaos-message-bus.service
casaos.service
casaos-user-service.service

 

Il dispositivo non e' velocissimo (ho preso la versione base per non spendere troppo) ma fa il suo lavoro

domenica 10 marzo 2024

ODM ed FFMPEG

E' possibile utilizzare ffmpeg per estrarre fotogrammi (a cadenza a scelta dell'utente) da un filmato MP4 per fornirli in pasto ad ODM. Dalle prove che ho fatto e' un sostituto idoneo a scattare una serie di foto 



il seguente comando estrae un fotogramma al secondo (parametro dopo t-prev_selected_t) e li salva nel folder /frames

ffmpeg -i 20240229_094358.mp4 -vf "select=bitor(gte(t-prev_selected_t\,1)\,isnan(prev_selected_t))" -vsync 0 ./frames/f%09d.jpg

Rete neurale ed Aruco

Per alcune prove con gli Aruco tags sto usando una camera che ha come formato di output JPG ma sarebbe piu' idoneo il formato PNG 

Ho provato la rete neurale a questo link che permette di ricostruire dettagli che si sono persi al momento della compressione JPG.

Ho usato un set di immagini processandole come segue

alias enhance='function ne() { docker run --rm -v "$(pwd)/`dirname ${@:$#}`":/ne/input -it alexjc/neural-enhance ${@:1:$#-1} "input/`basename ${@:$#}`"; }; ne'
for x in ./originali/*.jpg; do
    #echo $x
    enhance --zoom=1 --model=repair $x
done

e mettendo a confronto le coordinate degli stessi aruco tags nel set jpg ed in quello elaborato dalla rete neurale e salvato in png

Per dare un'idea la prima immagine e' un originale in JPG

 


la seconda e' la stessa immagine processata dalla rete neurale e salvata in PNG


 

Nei grafici sottostanti i risultati (nei grafici rosso=set PNG, blu=set JPG)

In sintesi le coordinate dei tag sono molto simili ma

1) il dato PNG non ha mostrato outliers ed errori come invece quello JPG

2) nel set PNG il riconoscimento di tag lontani era circa il doppio rispetto al set JPG

3) a parte i punti 1 e 2 il processing tramite rete neurale ma non ha migliorato in modo sensibile la qualita' dell'algoritmo di posizionamento dei tags


 






 

venerdì 8 marzo 2024

Realsense Docker

Aggiornamento:

Il container e' disponibile gia' compilato su Docker.com al mio account

https://hub.docker.com/repository/docker/c1p81/realsense_2004/general

 ============================================

Ho deciso di tirare fuori dal cassetto la D415 Realsense e come al solito montare l'SDK diventa sempre piu' difficile a causa delle politiche di Intel di dismissione dei propri dispositivi


 

Stavolta volevo provare la strada dell'ambiente docker ma ne' il container ufficiale ne' alcuni trovati su docker hub risultavano completamente funzionanti e me lo sono scritto da solo

(da modificare l'image_id)

Bash

docker run -it --rm  --privileged  -v /dev:/dev  -v "$HOME:/home/luca/"   --device-cgroup-rule "c 81:* rmw"     --device-cgroup-rule "c 189:* rmw"   b105279d1264 /bin/bash

Realsense Viewer

xhost +

docker run -d  --net=host --env="DISPLAY" --volume="$HOME/.Xauthority:/root/.Xauthority:rw"   -v /dev:/dev    --device-cgroup-rule "c 81:* rmw"     --device-cgroup-rule "c 189:* rmw"   2bd94ff6bc38 realsense-viewer

 

Save to disk

docker run -it --rm --privileged    -v /dev:/dev  -v "$HOME:/home/luca/"   --device-cgroup-rule "c 81:* rmw"     --device-cgroup-rule "c 189:* rmw"   34ea465a203e sh -c "cd /home/luca && rs-save-to-disk"

per creare il docker si usa il comando

docker build -t 20_04_real .

con il seguente Dockerfile


FROM public.ecr.aws/lts/ubuntu:20.04_stable

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update \
&& apt-get install -qq -y --no-install-recommends \
build-essential \
cmake \
lsb-release \
git \
curl \
libssl-dev \
libusb-1.0-0-dev \
pkg-config \
libudev-dev \
libgtk-3-dev \
libglfw3-dev \
libgl1-mesa-dev \
libglu1-mesa-dev \
curl \
python3 \
python3-dev \
python3-pip \
libopencv-dev \
python3-opencv \
python3-numpy \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*


RUN mkdir -p /etc/apt/keyrings
RUN curl -sSf https://librealsense.intel.com/Debian/librealsense.pgp | tee /etc/apt/keyrings/librealsense.pgp > /dev/null


RUN echo "deb [signed-by=/etc/apt/keyrings/librealsense.pgp] https://librealsense.intel.com/Debian/apt-repo `lsb_release -cs` main" | tee /etc/apt/sources.list.d/librealsense.list
RUN apt-get update


RUN apt-get -y install librealsense2-dkms librealsense2-utils librealsense2-dev librealsense2-dbg librealsense2-udev-rules mc nano locales
RUN apt-get clean
RUN pip install pyrealsense2

RUN git clone https://github.com/IntelRealSense/librealsense
RUN cd librealsense
RUN mkdir /librealsense/build
WORKDIR /librealsense/build
RUN cmake ../ -DBUILD_EXAMPLES=true
RUN make

Debugger integrato ESP32S3

Aggiornamento In realta' il Jtag USB funziona anche sui moduli cinesi Il problema risiede  nell'ID USB della porta Jtag. Nel modulo...