Sunteți pe pagina 1din 19

10.

Aplicaţia ghost surveillance


system
10.1. Scopul aplicaţiei Ghost Surveillance System
Ghost Surveillance System este o aplicaţie software bazată pe
platforma JAVA care realizează implementarea unui sistem de securitate.
Datele video sunt obţinute de la un dispozitiv de captură, cum ar fi un o cameră
video conectată la un TV Tuner. Software-ul dispune de un subsistem de
captură automată care (dacă este activat) salvează cadrele după detecţia
mişcării.
O altă parte a aplicaţiei permite accesul la cadrele salvate anterior.
Interfaţa utilizator a acestui subsistem permite accesul uşor şi intuitiv la arhiva
de imagini, mărirea şi listarea la imprimantă a imaginii selectate.
Aplicaţia dispune de un submeniu cu istoricul supravegherii care
furnizează informaţii vizuale despre subsistemul de captură automată.
Mediul de programare folosit este Java, mai exact JSDK 1.3. Pentru
partea de captură am folosit tehnologiile din Java Media Framework 2.1.1.
(vezi Capitolul 8). Funcţionarea corectă a aplicaţiei este condiţionată de
instalarea corectă a acestor pachete.

10.2. Prezentarea aplicaţiei


Pentru a evita accesul persoanelor neautorizate, la intrarea în aplicaţie,
utilizatorul este solicitat să introducă o parolă. Parola implicită este “UTCN”.
După introducerea corectă a parolei, apare ecranul de intrare în program, care
cuprinde sigla aplicaţiei şi informaţii legate de Copyright (Figura 10.1).

Figura 10.1.. Ecranul de intrare în aplicaţie


După iniţializarea camerei video, apare ecranul principal (vezi Figura
10.2 şi Figura 10.3). Acesta este format din:

1
• Fereastră Live video – afişează continuu imaginile primite de
la camera video. Rezoluţia la care se realizează afişarea este
320x240 pixeli. Vizualizarea poate fi întreruptă prin apăsarea
butonului Pause.
• Fereastră informaţii utile – afişează informaţii despre data şi
ora curentă, cât timp a rulat aplicaţia, numărul de cadre
salvate. Reîmprospătarea informaţiilor se face automat.
• Sigla aplicaţiei – este afişată în colţul dreapta sus.
• Reglaj senzitivitate – permite reglarea senzitivităţii
supravegherii. Valorile posibile sunt între 0 şi 100. O valoare
de 0 înseamnă că diferenţa dintre două cadre trebuie să fie de
100% pentru ca aplicaţia să salveze un cadru. O valoare de
100 înseamnă că diferenţa între cadre trebuie să fie 0 pentru
ca aplicaţia să salveze un cadru. În urma testelor a rezultat că
valoarea optimă pentru senzitivitate este aproximativ 65.
• Reglaj rată verificare – permite reglarea perioadei la care să
se facă compararea de cadre. Valorile posibile sunt între 1 şi
10. Valoarea 1 înseamnă că verificarea se face la fiecare 2
secunde, valoarea 10 înseamnă că verificarea se face la
fiecare 0.2 secunde.
• Istoric supraveghere (History) – furnizează informaţii utile
despre diferenţele între cadre. Este activat doar când
supravegherea este pornită. Cu o linie de culoare roşie este
afişat pragul de senzitivitate stabilit. Sub formă de
dreptunghiuri galbene sunt reprezentate diferenţele între două
cadre analizate succesiv. Dacă o diferenţă trece peste pragul
de senzitivitate, se salvează automat ultimul cadru analizat.
Este foarte util pentru calibrarea sistemului de supraveghere
având în vedere faptul că pot exista variaţii ale calităţii
semnalului.
• Intrare în submeniul Image – este un submeniu care permite
vizualizarea, mărirea şi listarea la imprimantă a imaginilor
capturate manual sau automat
• Captură manuală de cadru – la apăsare se realizează captura
unui cadru, cadru care este salvat într-un fişier ce include în
numele lui ora exactă (la precizie de milisecundă) la care s-a
realizat captura, fişier aflat într-un director cu numele data
curentă
• Buton pornire/oprire supraveghere – permite oprirea/pornirea
operaţiei de supraveghere. Implicit este Off.Salvarea
imaginilor se face în format .JPG.

2
Informaţii utile
Fereastră Siglă aplicaţie
Live video

Intrare în Reglaj
submeniul Image senzitivitate

Captură Reglaj rată de


manuală de verificare
cadru
Istoric
Buton supraveghere
pornire/oprire
supraveghere

Figura 10.2. Meniul principal cu supravegherea dezactivată

History
furnizează
Supravegherea informaţii
este activată

Figura 10.3. Meniul principal cu supravegherea activată

Prin apăsarea butonului Image din meniul principal se ajunge în


submeniul Image, unde se pot efectua unele operaţii asupra imaginilor,
indiferent dacă acestea au fost salvate manual sau automat. Fereastra este
compusă din:

3
• În partea din stânga sunt afişate imagini scalate (thumbnails)
ale imaginilor salvate. Prin selectarea uneia dintre aceste
imagini, în partea din dreapta a ecranului apare imaginea la
mărimea ei originală (vezi Figura 10.4).
• Sub imagine se dau informaţii despre data şi ora exactă la
care a fost capturată imaginea.

Imaginea
afişată
Preview Data şi ora
imagini şi capturării
selectare

Buton Zoom Buton Print

Buton Back
Buton Revert

Figura 10.4. Meniul vizualizare imagini

• Butonul de Zoom permite mărirea unei anumite zone a


imaginii. Mărirea zonei de interes implică selectarea cu
mouse-ul şi apăsarea butonului Zoom (vezi Figura 10.5)

Imaginea mărită

Figura 10.5. Meniul vizualizare imagini – imaginea mărită

4
• Prin apăsarea butonului Print aparea fereastra standard de
printare din Windows. Imprimarea se realizează pentru
imaginea originală sau imaginea mărită, în funcţie de
operaţiile efectuate asupra imaginii (vezi Figura 10.6).

Figura 10.6. Meniul vizualizare imagini – imprimare imagine

• Prin apăsarea butonului Back (vezi Figura 10.4) se revine la


meniul principal

Din meniul principal se pot accesa informaţii despre autorul aplicaţiei


şi despre motivele realizării ei. Pentru aceasta trebuie activată din meniu
fereastra HELP/ABOUT. Aici mai găsiţi o scurtă descriere a aplicaţiei, în limba
engleză.
Ceea ce ne interesează pe noi din această aplicaţie este modul cum se
realizează captura. În primul rând, camera trebuie să fie detectată cu JMF
Registry, care poate fi adus de la www.java.sun.com. Nu toate camerele sunt
detectate, dar în general probleme au apărut doar la camerele Logitech.

10.3. Interfaţa aplicaţiei cu camera video


Codul pentru detecţia şi iniţializarea camerei video este dat în
Exemplul 10.1. Pentru o bună înţelegere a tehnologiilor folosite, consultaţi
Capitolul 8.

5
Exemplul 10.1. Interfaţa aplicaţiei GSS cu camera video

/**
Aceasta clasa creaza un processor pentru dispozitivul de capura care a
fost inregistrat in prealabil cu JMF Registry si salveaza cadrele
capturate intr-un buffer.
*/
import javax.media.*;
import javax.media.format.YUVFormat;
import javax.media.format.JPEGFormat;
import java.util.*;
import javax.media.protocol.*;
import javax.media.datasink.*;
import java.io.IOException;
import java.awt.image.BufferedImage;
import java.awt.Component;
import javax.media.util.BufferToImage;
import javax.media.format.VideoFormat;
import java.awt.Image;

public class CameraInterface extends Thread implements


ControllerListener, DataSinkListener{
/**
clasa CameraImterface returneaza OK daca poate trimite
imaginea in buffer
*/
public static final int OK=0;
/**
clasa CameraImterface returneaza NOT_OK daca nu s-a
initiat inca
*/
public static final int NOT_OK=1;

private boolean state_transition_OK = true;


private int state=NOT_OK;
private boolean Proceesorstart=false;
private Buffer readBuffer=new Buffer();
private DataSource InitDataSource = null;
//aceasta este DataSource initiala pentru Camera
//private Processor processor;
private Player player;

6
private Object waitSync = new Object();
private DataSource out_data_source=null;
private DataSource rtpDataSource=null;
private DataSource cloneableDataSource=null;
private DataSourceHandler handler=new DataSourceHandler();
private BufferedImage clip_img=null;

/***********************************************************************
* Constructorul
***********************************************************************/

/**
Acesta este singurul constructor pentru CameraInterface.
Detecteaza dispozitivul captura si creeaza processorul pentru el
*/
public CameraInterface(){
//out_data_source = processor.getDataOutput();

try {
handler.setSource(ProcessorSetUp());
} catch (IncompatibleSourceException e) {
System.err.println("Cannot handle the output
DataSource from the processor: " + out_data_source);
}
handler.addDataSinkListener(this);

// Trece procesorul in starea Prefetch


//processor.prefetch();
//waitForState(processor.Prefetched);

System.out.println("Starts the processor");


handler.start();
//porneste procesorul
//processor.start();
player.start();
state=OK;

private DataSource ProcessorSetUp(){


CaptureDeviceInfo _cap_info=null;

7
YUVFormat VF=new
YUVFormat(YUVFormat.YUV_420);
Vector _device_list =
CaptureDeviceManager.getDeviceList(VF);

if (_device_list.size() > 0){


Enumeration _enu=_device_list.elements();
_cap_info = (CaptureDeviceInfo)_enu.nextElement();
_enu=null;
System.out.println(" Capture device info
:"+_cap_info.getName());
}else{
System.out.println("Cannot find a video capture device");
System.exit(0);
}
MediaLocator _mlocator=_cap_info.getLocator();
_cap_info=null;
if (_mlocator == null) {
System.err.println("Cannot build media locator from video
listener " );
System.exit(0);
}

// Creaza o DataSource pentru media locatorul dat


try {
InitDataSource = Manager.createDataSource(_mlocator);
cloneableDataSource=Manager.
createCloneableDataSource(InitDataSource);
}catch (Exception e) {
System.err.println("Cannot create DataSource from: " +
_mlocator);
System.exit(0);
}
_mlocator=null;
System.out.println("create processor for: " +
InitDataSource.getContentType());
//creaza un processor pentru DataSource-ul obtinut de
//la Camera
try {
//processor =
Manager.createProcessor(cloneableDataSource);

8
player=Manager.createPlayer(((SourceCloneable)
cloneableDataSource).createClone());
}catch (Exception e) {
System.err.println("Failed to create a processor from the
given DataSource: " + e);
System.exit(0);
}
// processor.addControllerListener(this);
player.addControllerListener(this);

// Trece processorul in starea configured


// processor.configure();
// waitForState(processor.Configured);

// primeste iesirea de la processor


// processor.setContentDescriptor(new
ContentDescriptor(ContentDescriptor.RAW));

// pune processorul in starea realize


// processor.realize();
// waitForState(processor.Realized);
return (cloneableDataSource);
}

/**
Aceasta metoda returneaza starea curenta a CameraInterface
*/
public int getStatus(){
return state;
}

public void run() {


}

public synchronized Component getPlayerVisual () {


return (player.getVisualComponent());
}

public synchronized Component getPlayerControl() {


return (player.getControlPanelComponent());
}

9
public synchronized BufferedImage getBufferedImage(){
BufferedImage bim=null;
VideoFormat vf;
synchronized(readBuffer){

vf=(VideoFormat)readBuffer.getFormat();
BufferToImage bufferToImage = new BufferToImage(vf);
bim= (BufferedImage)bufferToImage.createImage(readBuffer);
if(vf==null){System.out.println("Video format null");};

}
clip_img=bim;
return bim;
}

/**
Urmatoarea metoda returneaza imaginea creata din bufferul
de intrare
*/
public BufferedImage getImage(){
return clip_img;
}

/**
Aceasta metoda termina Camera Interface
*/
public void Delete(){
//processor.close();
handler.close();
InitDataSource.disconnect();
readBuffer=null;
}

public boolean canDelete(){


return true;
}

/**
Aceasta metoda opreste procesarea din buffer
*/

10
public void Stop(){
handler.Stop();
state=NOT_OK;
}

public void controllerUpdate(ControllerEvent evt) {


if (evt instanceof ConfigureCompleteEvent ||
evt instanceof RealizeCompleteEvent ||
evt instanceof PrefetchCompleteEvent) {
synchronized (waitSync) {
state_transition_OK = true;
waitSync.notifyAll();
}
}else if (evt instanceof ResourceUnavailableEvent) {
synchronized (waitSync) {
state_transition_OK = false;
waitSync.notifyAll();
}
}else if (evt instanceof EndOfMediaEvent) {
//processor.close();
}else if (evt instanceof SizeChangeEvent) { }
}

public void dataSinkUpdate(DataSinkEvent evt) {


if (evt instanceof EndOfStreamEvent) {
System.err.println("All done!");
evt.getSourceDataSink().close();
System.exit(0);
}
}

/**
Aceasta clasa citeste din DataSource si imparte streamul in buffere
*/

private class DataSourceHandler extends Thread implements


DataSink,BufferTransferHandler {
DataSource source;
PushBufferStream pushStrms[] = null;
private Vector listeners = new Vector(1);
//Stocheaza toate streamurile care nu s-au terminat

11
SourceStream unfinishedStrms[] = null;

/**
Seteaza DataSource pentru DataSourceHandler
*/
public void setSource(DataSource source) throws
IncompatibleSourceException {
if (source instanceof PushBufferDataSource) {
System.err.println("The source is push Buffer Data Source ");
pushStrms = ((PushBufferDataSource)source).getStreams();
unfinishedStrms = new SourceStream[pushStrms.length];
for (int i = 0; i < pushStrms.length; i++) {
pushStrms[i].setTransferHandler(this);
unfinishedStrms[i] = pushStrms[i];
}
}else
throw new IncompatibleSourceException();
this.source = source;
}

public void setOutputLocator(MediaLocator ml) {


}
public MediaLocator getOutputLocator() {
return null;
}

public String getContentType() {


return source.getContentType();
}

public void open() {


}

public void run() {


try {
source.start();
} catch (IOException e) {
System.err.println(e);
}
}

12
public void Stop(){
try{
source.stop();
}catch(IOException e){}
}

/**
Termina metoda DataSourceHandler
*/
public void close() {
try {
source.stop();
}catch (IOException e) {
System.err.println(e);
}
}

public void addDataSinkListener(DataSinkListener dsl) {


if (dsl != null)
if (!listeners.contains(dsl))
listeners.addElement(dsl);
}

public void removeDataSinkListener(DataSinkListener dsl) {


if (dsl != null)
listeners.removeElement(dsl);
}

protected void sendEvent(DataSinkEvent event) {


if (!listeners.isEmpty()) {
synchronized (listeners) {
Enumeration list = listeners.elements();
while (list.hasMoreElements()) {
DataSinkListener listener =
DataSinkListener)list.nextElement();
listener.dataSinkUpdate(event);
}
}
}
}

13
/**
Aceasta metoda este apelata cand vin date de la DataSource
*/
public void transferData(PushBufferStream stream) {
synchronized(readBuffer){
try {
stream.read(readBuffer);
}catch (IOException e) {
System.err.println(e);
sendEvent(new DataSinkErrorEvent(this, e.getMessage()));
return;
}

// Verificam sa vedem daca toate streamurile sunt gata


if (readBuffer.isEOM() && checkDone(stream)) {
sendEvent(new EndOfStreamEvent(this));
}
}
}

/*
Verificam daca toate streamurile sunt procesate
*/
public boolean checkDone(SourceStream strm) {
boolean done = true;
for (int i = 0; i < unfinishedStrms.length; i++) {
if (strm == unfinishedStrms[i])
unfinishedStrms[i] = null;
else if (unfinishedStrms[i] != null) {
// Cel putin un stream nu e gata
done = false;
}
}
return done;
}

public Object [] getControls() {


return new Object[0];
}

public Object getControl(String name) {

14
return null;
}
}
}

10.4. Detecţia mişcării


O altă problemă apărută la dezvoltarea acestei aplicaţii a fost modul de
detecţie a mişcării. Organigrama după care funcţionează sistemul de
supraveghere este prezentată în Figura 10.7.

15
Figura 10.7. Organigrama algoritmului de detecţie a mişcării

16
În Exemplul 10.2 este prezentat codul care calculează diferenţa între
mediile de culoare ale celor două imagini.

Exemplul 10.2. Codul care calculeaza diferenţa între imagini

//calculeaza pentru doua imagini diferenta dintre ele ca


//diferenta intre mediile de culoare ale celor doua imagini.
private int computeDifference (BufferedImage oldImage,BufferedImage
newImage) {
int [][] RGB;
int [] temp;
int width,height;
width=oldImage.getWidth();
height=oldImage.getHeight();
//se extrage codul rgb pentru imaginea de referinta
temp=new int[width*height];
RGB=new int[height][width];
temp=oldImage.getRGB(0,0,width,height,temp,0,width);
int index=0;
for (int i=0;i<height;i++) {
for (int j=0;j<width;j++) {
RGB[i][j]=temp[index++];
} }
//se face xor cu rgb pentru imaginea analizata(diferenta intre
componentele de culoare)
temp=newImage.getRGB(0,0,width,height,temp,0,width);
index=0;
for (int i=0;i<height;i++) {
for (int j=0;j<width;j++) {
RGB[i][j]=RGB[i][j]^temp[index++];
} }
//se calculeaza diferenta intre medii
int maxDifference=0; float medie=0;
int masc=(255<<16) | (255<<8) | (255);
for (int i=0;i<height;i++)
for (int j=0;j<width;j++)
medie+=RGB[i][j];
medie=medie/(width*height);
maxDifference=(int)medie;
float f=((float)maxDifference)/((float)masc);
return (int)(f*255);

17
}

10.5. Avantajele şi dezavantajele aplicaţiei GSS


Avantajele aplicaţiei GSS sunt următoarele:

• Aplicaţia poate fi rulată pe orice sistem care are instalate


JSDK 1.3 şi JMF 2.1.1. Avantajul este că aceste pachete sunt
gratuite, putând fi aduse prin download de la
www.java.sun.com.
• Salvarea imaginilor se face în format JPEG, care ocupă puţin
spaţiu pe disc (o imagine la rezoluţia de 320x240 are
aproximativ 20 kb). Avantajul este evident, şi anume necesar
minim de spaţiu pe disc. Pe un CD standard de 650 MB încap
aproximativ 32.000 de astfel de imagini.
• Salvarea imaginilor se face structurat. Numele directorului în
care se află imaginea conţine data la care s-a făcut captura, iar
numele fişierului conţine ora la care s-a făcut captura. Din
aceste motive căutarea unei imagini în baza de date este
foarte uşor de realizat.
• Sistemul pe care rulează aplicaţia poate fi folosit şi în alte
scopuri.
• Datorită mecanismului de funcţionare bine pus la punct un
intrus este detectat în mai puţin de 1 secundă, după care
analiza imaginii şi tipărirea la imprimantă durează mai puţin
de 1 minut.
• Dimensiunea aplicaţiei este foarte redusă (mai puţin de 100
kb).

Dezavantajele aplicaţiei GSS sunt următoarele:

• Cerinţe hardware ridicate. Datorită faptului că sunt necesare


multe comparaţii de coduri RGB, procesorul este destul de
solicitat.
• Aplicaţia a fost concepută să ruleze corect doar la rezoluţia de
1024x768, independent de numărul de culori. Folosirea
aplicaţiei la alte rezoluţii poate duce la erori de afişare sau
chiar la blocarea sistemului.
• API-ul Java Media Framework nu este suficient de bine pus
la punct. Din acest motiv aplicaţia se poate bloca după un
anumit timp de funcţionare.

18
19

S-ar putea să vă placă și