venerdì 8 aprile 2011

Videosorveglianza fai-da-te!

Vediamo oggi come sia possibile creare una semplice applicazione per fare videosorveglianza “fai-da-te” completamente gratuita! Ciò che otterremo alla fine sarà una semplice applicazione che manda immagini  prese da una webcam ad un web server a cui possiamo collegarci per visualizzare cosa sta succedendo magari a casa nostra, in nostra assenza. Và detto che questa semplice applicazione è a solo scopo “didattico” e può essere arricchita a vostro piacimento.


Il nostro progetto è così architettato:

In pratica un “Frame Grabber” prende le immagini da un webcam e le salva su di un file, un “Image Sender” manda tali immagini ad un Web Server remoto che le pubblicherà. A questo punto le immagini della webcam saranno disponibili ad un qualsiasi browser remoto che si collega al web server permettendoci di fare videosorveglianza. Per divertirmi ho scelto di utilizzare linguaggi diversi per ciascun modulo:
  •  Frame Grabber: grabbing di immagini da webcam tramite librerie Open CV (C++)
  • Image Sender: classe Java che effettua una POST dell’immagine tramite una Http connection
  • Image Receiver: script PHP che salva sul web server l’immagine ricevuta
  • Web Page: pagina HTML che visualizza l’immagine

Come si nota, l’applicazione viene sviluppata parte in C/C++ , parte in PHP e parte in Java: in pratica una webcam riprende una scena, le immagini vengono grabbate e salvate ad intervalli regolari su un file del pc di casa tramite le librerie Open CV (librerie largamente utilizzate nel campo della computer vision). Una semplice applicazione Java, prende tale immagine e la spedisce (POST) ad un web server. Il web server a questo punto pubblicherà l’immagine tramite un semplice script PHP e sarà visibile tramite per esempio un semplice browser.
Per sviluppare l’applicazione ho usato ambienti di sviluppo per C/C++ e Java quali Visual Studio 2010 ed Eclipse, ma ciò non è vincolante poichè tutto il codice è completamente portabile su qualsiasi SO. Si noti che sul pc di casa (quello su cui gira l’Image Sender), è necessario avere installato una Java Virtual Machine scaricabile da qui.

Image Receiver (PHP)
Prima di tutto bisogna scegliere un web server (free!) che supporti Scripting PHP. Qui potete trovare una lista di esempio. Sceglietene uno, registratevi e createvi un sito. A questo punto mettete sul server (p.es. via FTP) il seguente file PHP:

recv.php
<?php
/**
 * Save the submitted file to disk
 */
if (is_uploaded_file($_FILES['img']['tmp_name'])) {
    move_uploaded_file($_FILES['img']['tmp_name'], "img.jpg");
}

Questo script non fa altro che salvare su disco l’immagine che proviene da un client tramite HTTP POST su un file “img.jpg”. In questo modo potete pubblicare un’immagine su web. Sostituite la pagina di default del sito con la seguente:

index.html
<html>
<head>
<title>Remote Surveillance System</title>
</head>
<body>

<img name="frame" src="" border="0" />

<script type="text/javascript">
    function showFrame() {
        document.images.frame.src = "img.jpg?" + Math.random();
        setTimeout("showFrame()", 3000);
    }
    showFrame();
</script>

</body>
</html>

In pratica la pagina effettua il refresh automatico dell’immagine “img.jpg” ogni 3 secondi escludendo la cache. Il file index.html dovrebbe essere la pagina di default del nostro sito e deve risiedere sulla stessa cartella del file recv.php. La parte da caricare sul web server è terminata. Passiamo ora ad implementare le 2 applicazioni che gireranno sul pc di casa.

Image Grabber (C/C++)
A questo punto siamo pronti per scrivere il codice che grabba le immagini da una semplice webcam e le salva su disco su un file “img.jpg”. Utilizzeremo le Open CV, librerie che vengono utilizzate per la computer vision. In pratica è un pò riduttivo usare le Open CV solo per grabbare delle immagini da una web cam, ma ciò potrebbe rivelarsi utile nel caso si voglia arricchire l’applicazione con funzionalità quali motion detection, object tracking ecc. Scarichiamo e compiliamo le Open CV dal sito. Per la compilazione delle librerie lib e dll possiamo utilizzare CMake o direttamente Visual Studio secondo queste istruzioni. Una volta che ci siamo installati le Open CV, creaiamo un nuovo progetto con Visual Studio, una semplice Console Application, chiamiamolo “VideoSorveglianza”. Includiamo le directory di include opportune di Open CV nella sezione “C/C++ -> Additional Include Directories” (p.es. “..\OpenCV2.2\include\opencv”) e tutte le librerie statiche nella sezione “Linker -> Input->Addicional Dependencies” (p.es. ..\lib\cv220.lib ecc.). Copiamo le librerie dinamiche delle Open CV (p.es. cv220.dll ecc.) nella cartella Debug e/o Release del nostro progetto.  Creiamo un nuovo file all’interno del progetto:

VideoSorveglianza.cpp

#include <stdio.h>
#include "cv.h" /* file include Open CV */
#include "highgui.h" /* file include Open CV */

int main(int argc, char** argv)
{
        CvCapture *capture;
        IplImage  *frame;
        double    t, ms = 0;
        int       key;

        /* inizializza la webcam */
        capture = cvCaptureFromCAM(0); /* cattura il flusso video dal primo device del pc*/
        cvNamedWindow("video", 1);

        while (key != 'q') {
                t = (double)cvGetTickCount();

                /* display a video */
                frame = cvQueryFrame(capture); /* grabba l’immagine */
                cvShowImage("video", frame);
                key = cvWaitKey(1); /* aspetta input da tastiera 1 sec */

                /* calcola il tempo trascorso */
                t = (double)cvGetTickCount() - t;
                ms += t/((double)cvGetTickFrequency() * 1000.0);

                /* autosave dell’immagine ogni 3 secondi */
                if (ceil(ms) >= 3000) {
                        cvSaveImage("img.jpg", frame);
                        ms = 0;
                }
        }

        /* free memory */
        cvReleaseCapture(&capture);
        cvDestroyWindow("video");

        return 0;
}

A questo punto, dopo aver compilato il progetto e collegato la webcam, possiamo lanciare l’eseguibile da console tramite il comando: “VideoSorveglianza.exe”. Il risultato è il seguente:


Tale immagine verrà salvata sul file locale “img.jpg” ad intervalli regolari di 3 secondi.

Image Sender (Java)
Il prossimo passo consiste nel creare il sender che spedirà le immagini ad un web server. Si noti che la spedizione deve avvenire come se fosse un HTTP POST. La classe Java (che girerà anch’essa sul nostro pc di casa), semplicemente legge l’immagine dal file “img.jpg”, apre una connessione URL col web server e la spedisce in formato binario. Apriamo Eclipse e creiamo un nuovo progetto, chiamiamolo p.es. “VideoSorveglianzaSender”. Creiamo una classe Sender con questo codice:

Sender.java

import java.net.*;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.*;

import javax.imageio.ImageIO;

public class Sender
{
    static URL u;
    private final String CrLf = "\r\n";
   
   
    void updateImage(){
       URLConnection conn = null;
       OutputStream os = null;
       InputStream is = null;
       try{
//Qui va sostituito l’indirizzo del web server: attenzione recv.php va lasciato
             URL url = new URL("http://<webserverURL>/recv.php");
             System.out.println("url:" + url);
             conn = url.openConnection();
             conn.setDoOutput(true);

// legge l’immagine da file e la mette su un array di byte
             File file = new File("img.jpg");
             Image image = ImageIO.read(file);
             BufferedImage bu = new
BufferedImage(image.getWidth(null),image.getHeight(null),BufferedImage.TYPE_INT_RGB);
             Graphics g = bu.getGraphics();
             g.drawImage(image,0,0,null);
             g.dispose();
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
             ImageIO.write(bu, "jpg", baos);
             byte []imgData = baos.toByteArray();

// crea la request http

             String message1 = "";
             message1 += "-----------------------------4664151417711" + CrLf;
             message1 += "Content-Disposition: form-data; name=\"img\"; filename=\"img.jpg\"" + CrLf;
             message1 += "Content-Type: image/jpeg" + CrLf;
             message1 += CrLf;
      
             // l’immagine viene spedita come multipart message.
      
             String message2 = "";
             message2 += CrLf + "-----------------------------4664151417711--" + CrLf;
      
             conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=---------------------------4664151417711");
             conn.setRequestProperty("Content-Length", String.valueOf((message1.length() + message2.length() + imgData.length)));
                   
             System.out.println("open os");
             os = conn.getOutputStream();
             System.out.println(message1);
             os.write(message1.getBytes());
      
             // SEND THE IMAGE
             int index = 0;
             int size = 1024;
             do{
                    System.out.println("write:" + index);
                    if((index+size)>imgData.length){
                           size = imgData.length - index;
                    }
                    os.write(imgData, index, size);
                    index+=size;
             }while(index<imgData.length);
                   
             System.out.println("written:" + index);
             System.out.println(message2);
             os.write(message2.getBytes());
             os.flush();
      
              // legge la risposta del web server
              System.out.println("open is");
             is = conn.getInputStream();
      
             char buff = 512;
             int len;
             byte []data = new byte[buff];
             do{
                    System.out.println("READ");
                    len = is.read(data);
                    if(len > 0){
                           System.out.println(new String(data, 0, len));
                    }
             }while(len>0);
      
             System.out.println("DONE");
      
       }catch(Exception e){
             e.printStackTrace();
       }finally{
             System.out.println("Close connection");
             try{
                    os.close();
             }catch(Exception e){}
             try{
                    is.close();
              }catch(Exception e){}
       }
    }
   
    public static void main(String args[]) throws IOException
    {
       while (true)
       {
             Sender send = new Sender();
             send.updateImage();       
             try {
// manda un immagine ogni 2 secondi
                    Thread.sleep(2000);
             } catch (InterruptedException e) {
                    e.printStackTrace();
             }
       }
    }
}

Sostituire nel punto segnato <webserverURL>, l’indirizzo URL del web server che abbiamo scelto all’inizio, avendo cura di chiamare lo script recv.php. La classe non fa altro che leggere il file img.jpg e metterlo dentro un byte array. A questo punto viene aperta una HTTP connection verso il web server e viene spedita l'immagine come "multipart message". L'image sender scriverà a video la risposta ottenuta dal web server.
A questo punto compiliamo la classe e copiamo il file “Sender.class” nella stessa directory Debug/Release di dove girerà il grabber “VideoSorveglianza.exe”.

Test finale
Proviamo il tutto: lanciamo l’eseguibile del grabber (dentro la cartella Debug/Release della solution di Visual Studio): “VideoSorveglianza.exe”. A questo punto lanciamo il sender tramite il comando “java Sender” lanciato dalla stessa cartella del progetto VideoSorveglianza. Aprendo un browser all’indirizzo del nostro sito vedremo le immagini provenienti dalla webcam che si aggiorneranno automaticamente. In questo modo potremmo controllare casa nostra magari direttamente dall’ufficio aprendo semplicemente un browser.

0 commenti:

Posta un commento

 
Design by Free WordPress Themes | Bloggerized by Lasantha - Premium Blogger Themes | cna certification