Terinspirasi dengan tulisan tentang memasang sendiri sistem cctv dengan zoneminder saya jadi iseng membuat sistem serupa (bukan memasang lagi, ini membuat). Awalnya memang tidak berniat untuk ke arah sana, hanya ingin melakukan interpretasi citra dengan memanfaatkan webcam, tapi kemudian terhambat karena framerate yang lambat ketika awal-awal mencoba.
Pendahuluan
Oke, mari dimulai percobaannya. Spesifikasi kita kali ini adalah membuat sistem cctv yang mengambil gambar dari webcam dan dapat diakses melalui web server (ujung yang cukup generik, bisa diperluas dengan membuat mobile client yang mengakses http :D). Selain itu tiap gambar diarsipkan sehingga memungkinkan untuk pemrosesan yang tidak membutuhkan tenggat. Agar lebih sederhana, kita akan mendekomposisi sistem yang kita buat menjadi 3 modul :
- program pengakses webcam (GUI).
- program perantara (CLI, bisa jadi opsional, nanti akan dijelaskan alasannya).
- halaman web (HTML+PHP)
Alat dan Bahan
Berikutnya alat yang diperlukan :
- web server (saya menggunakan xampp 1.5). modul yang diperlukan sebetulnya hanya apache dan php. database tidak diperlukan saat ini untuk menyederhanakan program.
- compiler delphi (anda bisa menggunakan apapun sebetulnya, tapi saya menggunakan delphi)
- A win32 machine. ya ya.. saya pakai API Windows tapi nggak pakai DirectX, cuma pakai VFW (Video for Windows). stok lama.
- Webcam. USB atau apapun. sekali-lagi, demi kesederhanaan, saya hanya menggunakan 1 webcam (default) yang ada di laptop. π
Arsitektur sistem
gambaran umum sistem bisa dilihat dalam gambar berikut.
Program 1,2, dan 3 sesuai dengan penjelasan di awal.
Program 1
Program ini dibuat menggunakan delphi dan terdiri dari 1 modul webcam dan 1 form. berikut ini kode untuk unit pengakses webcam (konstanta dan prosedur sudah di-dietkan supaya hanya mengandung yang diperlukan saja).
unit _cam; interface uses Windows, Messages, Controls, ExtCtrls; const WM_CAP_START = WM_USER; WM_CAP_DRIVER_CONNECT = WM_CAP_START + 10; WM_CAP_DRIVER_DISCONNECT = WM_CAP_START + 11; WM_CAP_SAVEDIB = WM_CAP_START + 25; WM_CAP_COPY = WM_CAP_START + 30; WM_CAP_SET_SCALE = WM_CAP_START + 53; WM_CAP_SET_PREVIEW = ( WM_CAP_START + 50 ); WM_CAP_SET_OVERLAY = ( WM_CAP_START + 51 ); WM_CAP_SET_PREVIEWRATE = ( WM_CAP_START + 52 ); type TCam = class protected hWndC: HWND; FIsCapturing: Boolean; procedure BeginCapture( Container: TWinControl ); procedure EndCapture; public constructor Create( Container: TWinControl ); destructor Free; property Handle:HWND read hwndc; property Capturing:boolean read FIsCapturing; procedure CopyToClipboard; procedure SaveToFile( filename: string ); end; implementation uses ClipBrd; function capCreateCaptureWindowA( lpszWindowName: PCHAR; dwStyle: longint; x: integer; y: integer; nWidth: integer; nHeight: integer; ParentWin: HWND; nId: integer ): HWND; stdcall external 'AVICAP32.DLL'; { TCam } procedure TCam.BeginCapture( Container: TWinControl ); begin hWndC := capCreateCaptureWindowA( 'Capture Window', WS_CHILD or WS_VISIBLE, Container.Left, Container.Top, Container.Width, Container.Height, Container.Handle, 0 ); if hWndC <> 0 then begin SendMessage( hWndC, WM_CAP_DRIVER_CONNECT, 0, 0 ); SendMessage( hWndC, WM_CAP_SET_SCALE, 1, 0 ); SendMessage( hWndC, WM_CAP_SET_PREVIEWRATE, 24, 0 ); SendMessage( hWndC, WM_CAP_SET_PREVIEW, 1, 0 ); FIsCapturing := True; end; end; procedure TCam.CopyToClipboard; begin if hWndC <> 0 then begin SendMessage( hwndc, WM_CAP_COPY, 0, 0 ); end; end; procedure TCam.EndCapture; begin FIsCapturing := False; if hWndC <> 0 then begin SendMessage( hWndC, WM_CAP_DRIVER_DISCONNECT, 0, 0 ); hWndC := 0; end; end; procedure TCam.SaveToFile( filename: string ); begin if hWndC <> 0 then begin SendMessage( hWndC, WM_CAP_SAVEDIB, 0, longint( pchar( filename ) ) ); end; end; constructor TCam.Create(Container: TWinControl); begin inherited Create; BeginCapture(Container); end; destructor TCam.Free; begin EndCapture; end; end.
sedangkan kode untuk formnya adalah sebagai berikut. satu hal yang perlu diperhatikan. form berikut saya buat StayOnTop karena kalau di-minimize atau tersembunyi, gambarnya tidak akan di-update. Hal lainnya adalah pengaturan webcam agar bisa didapat framerate yang tinggi (di laptop saya setelah beberapa fitur dimatikan seperti low-light boost dan color boost baru bisa berjalan di 25 FPS).
//unit1.dfm object Form1: TForm1 Left = 192 Top = 107 BorderStyle = bsDialog Caption = 'Form1' ClientHeight = 241 ClientWidth = 321 Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] FormStyle = fsStayOnTop OldCreateOrder = False OnCloseQuery = FormCloseQuery OnCreate = FormCreate PixelsPerInch = 96 TextHeight = 13 object Panel1: TPanel Left = 0 Top = 0 Width = 320 Height = 240 BevelOuter = bvNone Color = clBlack TabOrder = 0 end object _proc: TTimer Enabled = False Interval = 48 OnTimer = _procTimer Left = 56 Top = 16 end end
dan code-behind-nya adalah sebagai berikut.
unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, MPlayer, StdCtrls, citra, Spin, ClipBrd, _cam, jpeg, inifiles; type TForm1 = class( TForm ) Panel1: TPanel; _proc: TTimer; procedure FormCreate( Sender: TObject ); procedure FormCloseQuery( Sender: TObject; var CanClose: Boolean ); procedure _procTimer( Sender: TObject ); private cam: TCam; bmp: TBitmap; jpg: TJpegImage; ini: TIniFile; public end; var Form1 : TForm1; capmode : integer; datapath : string; implementation {$R *.DFM} { TForm1 } procedure TForm1.FormCreate( Sender: TObject ); begin ini := TIniFile.Create( extractfilepath( application.ExeName ) + 'config.ini' ); capmode := ini.ReadInteger( 'server', 'mode', 0 ); if capmode = 2 then begin datapath := ini.ReadString( 'server', 'datadir', 'data' ); if pos( ':', datapath ) = 0 then datapath := extractfilepath( application.ExeName ) + datapath; end; jpg := TJpegImage.Create; jpg.CompressionQuality := ini.ReadInteger('server','jpegquality', 40); _proc.Interval := 1000 div ini.ReadInteger('server', 'fps', 5); bmp := TBitmap.Create; if not Assigned( cam ) then cam := TCam.Create( Panel1 ); _proc.Enabled := cam.Capturing; end; procedure TForm1.FormCloseQuery( Sender: TObject; var CanClose: Boolean ); begin CanClose := False; _proc.Enabled := false; jpg.Free; bmp.Free; if Assigned( cam ) then cam.Free; CanClose := True; end; procedure TForm1._procTimer( Sender: TObject ); begin if cam.Capturing then begin if capmode > 0 then begin cam.CopyToClipboard; if capmode = 2 then begin bmp.Assign( clipboard ); jpg.Assign( bmp ); jpg.SaveToFile( format( '%s\%.12d.jpg', [datapath, gettickcount] ) ); end; end; end; end; end.
Program di atas akan menyimpan hasil rekaman dari webcam ke dalam file dalam format JPEG yang kualitasnya dapat diparameterkan sehingga ukuran file keluarannya dapat juga diatur. Nama file rekaman menggunakan gettickcount yang dikodekan menjadi string sepanjang 12 digit. Penggunaan angkan dimaksudkan agar memudahkan dalam mencari file terakhir tanpa harus menggunakan database. Untuk sementara semua hasil rekaman dari webcam disimpan dalam satu direktori (datadir). Selanjutnya bisa menggunakan struktur direktori yang mencerminkan tanggal (YYYY\MM\DD\) untuk memudahkan pengarsipan.
program di atas menggunakan file konfigurasi dengan nama config.ini yang lokasinya sama dengan lokasi executable.
[server]
#mode capture
#0:no save
#1:save to clipboard
#2:save to file (jpeg)
mode=2
datadir=data
jpegquality=40
fps=10
konfigurasi di atas akan membuat program webcam menyimpan 10 gambar tiap detik dengan kualitas kompresi sekitar 40 (untuk resolusi webcam 320×240 membutuhkan sekitar 5KB). Dari hasil hitung-hitungan. kalau 1 detik 10 gambar dikali 5 KB jadi 50. satu jam perlu 180MB. satu hari perlu 4GB. sedangkan 1 tahun perlu ~1.5TB. angka-angka tersebut adalah gambaran kasar dengan asumsi tiap detik direkam (biarpun tidak ada perubahan). untuk analisis lebih lanjut (membuat gambar-gambar yang tidak berubah) bisa dilakukan dengan membuat program lain yang bekerja pada direktori tersebut ^_^.
Web Interface
Berikutnya adalah program nomor 3 yaitu halaman web yang terdiri dari dua file yaitu index.html dan getimg.php.
<html> <head> <meta http-equiv="refresh" content="3" /> </head> <body> <img src="getimg.php"/></body> </html>
<?php //file: getimg.php $t = time().'.txt'; $result = system('cctv "C:\Program Files\Borland\Delphi7\Projects\cctv\data" > '.$t); if (file_exists($t)){ $result = file_get_contents($t); $img = urldecode(str_replace('%0D%0A', '', urlencode($result))); header('Content-type:image'); echo file_get_contents($img); unlink($t); } ?>
Perantara
Yang terakhir adalah program perantara. Ada alasan khusus mengapa saya membuat program perantara dibanding menjadikannya langsung dalam skrip PHP yaitu yang pertama terpikir. hehehe.. persoalannya adalah mengambil file terakhir (timestamp terbesar) dalam direktori yang membutuhkan iterasi ke tiap file (silakan coba buat versi skrip php-nya dan bandingkan dengan versi pemanggilan command-line).
program cctv; {$APPTYPE CONSOLE} uses SysUtils; var tdir : string; procedure pandu; begin writeln( 'ERR: use cctv <datadir>' ); end; function getlastfile( dir: string ): string; var sr : TSearchRec; FileAttrs : integer; tmp : string; mask : string; cur, last : longint; begin result := ''; last := -1; mask := '*.*'; FileAttrs := faAnyFile or faDirectory; if FindFirst( dir + '\' + mask, FileAttrs, sr ) = 0 then begin repeat if ( Pos( '.', sr.Name ) <> 1 ) then begin tmp := dir + '\' + sr.Name; trystrtoint( changefileext( sr.Name, '' ), cur ); if cur > last then begin result := tmp; last := cur; end; end; until FindNext( sr ) <> 0; FindClose( sr ); end; end; begin if ParamCount < 1 then pandu; tdir := getlastfile( paramstr( 1 ) ); if tdir <> '' then writeln( tdir ); end.
Penutup
Akhirnya selesai juga. pesan terakhir dari saya, tidak ada skinsyut hasil program karena subjek yang dijadikan skrinsyutnya (saya) sudah kusut :D. silakan dicoba sendiri, kalau ada yang nggak berhasil silakan komentar di sini (belum tentu saya bantu juga sih :D).
ah kecewa tak ada skrinsut xD
mas bagaimana cara mendeteksi warna pada live webcam
trims very much, it safe my live..
mas bisa lebih detail ga mas.. tolong kirimin ke email aku mas
astaga, sudah segitu banyaknya kode masih dianggap belum detail?
orang2 kaya anda,yang dibutuhkan negara ini…kalau anda calon presiden anda lah yang gw pilih…thanks for posting this post…
regard
Dian
Lebih bagus lagi anda menambah screen shootnya..
GO IGOS..
btw,anda orang open sourcejuga ya?
waktu post ini ditulis, yang ada skrinsut yang ‘tidak pantas’ dikonsumsi umum jadi skrinsutnya nggak ditampilin *ngeles* π
waduh, ogah ah jadi presiden..
mm, saya nggak tau orang opensource itu yang kayak apa.. π saya cuma bagi2 source code doang (sebetulnya sih cuma catatan pribadi karena nggak tau mo ngisi blognya sama apa hehehe).. π
pebbie,ada refrensi tentang webcam or program diatas?
itu pake referensi lama VFW (Video for Windows).. ada di MSDN π
pebbie..saya sedang tugas akhir,tapi saya menggunakan VB itu doank bedanya..saaya juga menggunakan PHP..kalau saya buat motion detection,kalau ada gerak baru webcam ambil gambar..tidak secara terus menerus..dan saya ambil cara dengan membandingkna 2 gambar..pebbie minta saran , tips , tutorial and lain – lainnya donk karena pebbie pernah buat project kaya diatas itu…Thanks ya pebbie..Btw , ada FB tidak?
Regards
Ian
gak ada contoh gambarnya ya mas (photo) ?
hehe…
nice.
saya suka..
keep movin’ forward kk!!
kalo pake vcl tscap32 bs g kak..?
thx
bisa juga kok pake tscap32, yang penting kan dapet gambarnya π