mercoledì 12 aprile 2023

Trasformazione affine con OpenCV

 Alla fine degli anni 80 se non erro alla Domenica Sportiva c'era un  primordiale sistema di computer grafica che consentiva di valutare il fuorigioco in una partita di calcio da immagini televisive....mi ero sempre chiesto come era possivile....dopo tanto tempo ho visto che e' possibile tramite la trasformazione affine 

 Partiamo da una immagine generica dello stadio di Firenze


 Le dimensione del campo dell'Artemio Franchi sono 105x68 m mentre nell'immagine sottostante sono riportate le dimensioni ufficiali del campo da calcio


 La trasformazione affine serve a trasformare le coordinate immagine in un altro sistema di coordinate (in questo caso coordinate centrimetriche reali con origine degli assi nella bandierina del calcio d'angolo in basso a sinistra)
OpenCV richiede tre punti ...per esempio la bandierina del calcio d'angolo in alto a sinistra ha coordinate pixel di 362x208 mentre coordinate reali 10500,6800 cm. I punti sono stati selezionati sull'incrocio di alcune linee dell'area di rigore
Nell'immagine successiva il risultato dell'algoritmo...non perfetto perche' le linee dovrebbero essere ortogonali ma considerando le incertezze direi che non e'male

 
import matplotlib.pyplot as plt
import numpy as np
import cv2 as cv

img = cv.imread('fiorentina.jpg')
pts1 = np.float32([[193,132],[362,208],[542,214]])
pts2 = np.float32([[10500,6800],[9950,4315],[10500,3765]])

M = cv.getAffineTransform(pts1,pts2)
dst = cv.warpAffine(img,M,(10500,6800))

plt.imshow(dst)
plt.show()
plt.savefig("dest.png")
plt.close(dst)


 

mercoledì 29 marzo 2023

PostgresSql partitioning

Una delle funzioni meno comuni di Postgres e' quella di effettuara il partiniong di una tabella per velocizzare il tempo di accesso. In sintesi si puo' suddividere una tabella di generose dimensione sulla base di un criterio (data, indice numerico intero) in sotto tabelle senza modificare la query di interrogazione della tabella madre. Non tutte le strutture dati sono idonee al partizionamento (per esempio quando ci sono solo campi stringhe)

In pratica si puo' creare un partitioning inserendo il campo che si vuole utilizzare per il partitioning

CREATE TABLE public.partizione (

    code character varying(12),
    vel real,
    v_stdev real,
  ....
    objectid integer NOT NULL,
    the_geom public.geometry(Point,32633)
) PARTITION BY RANGE(objectid);

 si creano quindi le sottotabelle con gruppi di 20000....attenzione che se il valore del campo di partizionamento eccede il valore massimo la riga non verra' inserita...per esempio inserire un campo con objectid=290000 generera' errore con la regola sottostante

CREATE TABLE partizione_0 PARTITION OF partizione FOR VALUES FROM (1) TO (20000);
CREATE TABLE partizione_1 PARTITION OF partizione FOR VALUES FROM (20001) TO (40000);
CREATE TABLE partizione_2 PARTITION OF partizione FOR VALUES FROM (40001) TO (60000);
CREATE TABLE partizione_3 PARTITION OF partizione FOR VALUES FROM (60001) TO (80000);
CREATE TABLE partizione_4 PARTITION OF partizione FOR VALUES FROM (80001) TO (100000);
CREATE TABLE partizione_5 PARTITION OF partizione FOR VALUES FROM (100001) TO (120000);
CREATE TABLE partizione_6 PARTITION OF partizione FOR VALUES FROM (120001) TO (140000);
CREATE TABLE partizione_7 PARTITION OF partizione FOR VALUES FROM (140001) TO (160000);
CREATE TABLE partizione_8 PARTITION OF partizione FOR VALUES FROM (160001) TO (180000);
CREATE TABLE partizione_9 PARTITION OF partizione FOR VALUES FROM (180001) TO (200000);
CREATE TABLE partizione_10 PARTITION OF partizione FOR VALUES FROM (200001) TO (220000);
CREATE TABLE partizione_11 PARTITION OF partizione FOR VALUES FROM (220001) TO (240000);
CREATE TABLE partizione_12 PARTITION OF partizione FOR VALUES FROM (240001) TO (260000);
CREATE TABLE partizione_13 PARTITION OF partizione FOR VALUES FROM (260001) TO (280000);

Per interrogare la tabella si usa SELECT..FROM TABLE partizione...sara' poi il motore del DB ad andare a trovare la sottotabella corrispondente

Ho cercato di misurare l'aumento delle prestazione del database ma ho avuto risultati variabili anche a causa di non riuscire a controllare la cache...indicativamente il miglior risultato e' stata un diminuzione dei tempi del 30%

giovedì 16 marzo 2023

Phishing via SMS 2023

 Una variante interessante rispetto al precedente esempio di phishing via SMS visto in precedenza 

Questa volta e' stato piu' insidioso perche' viene indicata la consegna di un pacco ad un centro di ritiro...plausibile...chi e' che non riceve pacchi oggi


Andando al link viene segnalata la necessita' di pagare 2 euro di tasse doganali (plausibile...mi e' capitato di ricevere pacchi dall'estero con la necessita' di pagare le tasse)




Mi sono insospettito quando mi veniva richiesto di reinserire i miei dati personali (del resto il corriere li doveva gia' conoscere....ho guardato il sito awardau.live..e sono passato ad aprire il link sul computer per vedere i sorgenti della pagina. La cosa divertente e' andando a www.awardau.live veniva proposto una spedizione di DHL mentre il link dell'SMS indicava una spedizione GLS


Capito l'ingannno ho provato a vedere come funziona la truffa....dopo aver cliccato sul sito awardau.live si veniva instradati su Pagamento Sicuro (ovviamente finto) in cui vengono richieste tutte le informazioni sulla carta di credito (ovviamente non era presente il pagamento via Paypal) compreso il CIN.

Nel codice HTML oltre a riferimenti a JQuery era presente il link a https://track.fwdtrck.com/click
(il link e' disattivo al momento in cui scrivo) ed a pro.ip-api.con con una key scaduta





Pulsante Dyson V10

 Pagare oltre 400 euro per avere un aspirapolvere e dopo tre anni essere costretti a smontarlo per la rottura di pezzo di plastica (il pulsante) da pochi centesimi di euro non fa certo piacere....fra le altre cose per la riparazione si deve smontare praticamente tutto il dispositivo (e si deve almeno una chiave torx con prolunga)

Il pezzo di ricambio corregge la debolezza di progettazione iniziale





mercoledì 15 marzo 2023

Local Packages in Golang

 Per poter utilizzare packages locali (librerie di funzioni create dall'utente esterno racchiuse in un package esterno al main) la soluzione e' inserire i le librerie esterne nel subfolder /vendor



nel file main.go l'importazione avviene semplicemente inserendo in import il nome del file della libreria e richiamando le funzioni della libreria come metodi 


import (
    "fmt"
    "scrivi"
)

func main() {
    fmt.Println("luca")
    scrivi.Debug(true)
    scrivi.Log("log")
}


lunedì 13 marzo 2023

Restful server in GO

Un esempio di server Restful con la libreria  go-restful

Nelle note del codice alcune indicazioni su come interagire con il server che si apre su porta 8080


// Pacchetto per utilizzo di Restfull
package main

// inserisci nuovo utente
// curl -X POST -H "Content-Type: application/json" -d '{"Id": "42", "Name": "Luca Innocenti"}' http://localhost:8080/users/
// update utente
// curl -X PUT -H "Content-Type: application/json" -d '{"Id": "42", "Name": "Luca Innocenti"}' http://localhost:8080/users/
// cancella utente con id 42
// ricerca utente con id 42
// curl -X "GET"  http://localhost:8080/users/42

//curl -vX POST http://server/api/v1/places.json -d @testplace.json

import (
    "fmt"
    "log"
    "net/http"

    restful "github.com/emicklei/go-restful/v3"
)

// User is a struct
type User struct {
    Id, Name string
}

// Fprint formats using the default formats for its operands and writes to w.
// Spaces are added between operands when neither is a string.
func New() *restful.WebService {
    service := new(restful.WebService)
    service.
        Path("/users").
        Consumes(restful.MIME_XML, restful.MIME_JSON).
        Produces(restful.MIME_XML, restful.MIME_JSON)

    service.Route(service.GET("/{user-id}").To(FindUser))
    service.Route(service.POST("").To(CreateUser))
    service.Route(service.PUT("/{user-id}").To(UpdateUser))
    service.Route(service.DELETE("/{user-id}").To(RemoveUser))

    return service
}

// GET
func FindUser(request *restful.Request, response *restful.Response) {
    id := request.PathParameter("user-id")
    // here you would fetch user from some persistence system
    usr := User{Id: id, Name: "Luca"}
    response.WriteEntity(usr)
}

// PUT /user-id
func UpdateUser(request *restful.Request, response *restful.Response) {
    parametri := User{Id: request.PathParameter("user-id")}
    //fmt.Println("udpdate user")
    fmt.Println("Numero " + parametri.Id + "Nome " + parametri.Name)
    usr := new(User)
    err := request.ReadEntity(&usr)
    // here you would update the user with some persistence system
    if err == nil {
        response.WriteEntity("modificato")
    } else {
        response.WriteError(http.StatusInternalServerError, err)
    }
}

// POST

func CreateUser(request *restful.Request, response *restful.Response) {
    usr := new(User)
    err := request.ReadEntity(&usr)
    // here you would create the user with some persistence system
    if err == nil {
        response.WriteEntity(usr)
    } else {
        response.WriteError(http.StatusInternalServerError, err)
    }
}

// DELETE /user-id

func RemoveUser(request *restful.Request, response *restful.Response) {
    fmt.Println(request.PathParameter("user-id"))
    log.Printf("DELETE")
    // here you would delete the user from some persistence system
}

func main() {
    restful.Add(New())
    log.Fatal(http.ListenAndServe(":8080", nil))
}






venerdì 10 marzo 2023

cUrl is not cUrl in Windows 10

 Usando praticamente mai Windows come ambiente di sviluppo ed ho scoperto a mie spese che il comando curl installato di default su Windows 10 non e' lequivalente di curl su Linux ma un alias di un comando di PowerShell con una sintassi differente

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...