Oczywiście obrazek zawsze można umieścić w systemie plików, a w samej bazie zapisać ścieżkę dostępu. Jednak w ten sposób musimy dbać o dwie bazy: relacyjną i plikową. Może to powodować niepotrzebne komplikacje i z punktu widzenia szybkości działania programu jest rozwiązaniem wolniejszym.
W tym wpisie zostanie pokazane jak przygotować małą, graficzną bazę danych w oparciu o CImg i Sqlite.
Biblioteka Sqlite świetnie nadaje się do takich zastosowań, ponieważ działa w obrębie tworzonego programu - baza danych jest wbudowywana w aplikację. Jest to zupełnie inne podejście, aniżeli to spotykane w dużych, komercyjnych bazach danych działających w trybie klient-serwer. W tym wypadku mamy bazę plikową, która jest zalecana do pracy z jednym klientem.
Aby rozpocząć pracę z Sqlite należy zainstalować następujące pakiety (przykład dla Ubuntu):
sudo apt-get install sqlite3 libsqlite3-0 libsqlite3-dev
Następnie utworzymy testową bazę danych, a w niej jedną tabelę do gromadzenia obrazów. Będą one zapisywane w polu photo typu blob.
sqlite3 image_db.sqlite3 create table photos(id integer primary key autoincrement, photo blob, filename char, dimx integer, dimy integer, dimz integer, dimv integer); .q
Poniżej został zaprezentowany przykładowy program pozwalający na dodawanie obrazów do bazy danych oraz ich odczyt. Analiza kodu oraz komentarzy w nim zawartych powinna umożliwić szybkie zrozumienie jego logiki działania.
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
#include "CImg.h"
using namespace std;
using namespace cimg_library;
int main(int argc, char **argv){
sqlite3 *db;
char *zErrMsg = 0;
int rc;
int dx,dy,dz,dv;
int rownum=0, num_bytes;
const unsigned char *filename;
const unsigned char *data_buffer;
//Parametry wejściowe
const char* file_i = cimg_option("-i", (char*)0, "Input image");
const char* db_name = cimg_option("-d", (char*)0, "Database file");
const int option = cimg_option("-o", 1,"Option (1-show, 2-add)");
//Sprawdzamy poprawność parametrów wejściowych
if (option != 1 && option != 2)
throw CImgException("Please specify correct option (1-show, 2-add)");
if (db_name==(char*)0)
throw CImgException("Please specify database file (option -d)");
if (option == 2 && file_i==(char*)0) {
throw CImgException("Please specify image file name (option -i)");
exit(1);
}
//Otwarcie bazy danych
rc = sqlite3_open(db_name, &db);
if( rc ){
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
//Definicja zapytań
sqlite3_stmt *insert_stmt, *select_stmt;
char insert_str[] = "insert into photos(photo,filename,dimx,dimy,dimz,dimv) values (?,?,?,?,?,?)";
char select_str[] = "select id, photo,filename,dimx,dimy,dimz,dimv from photos";
char begin_str[] = "begin transaction";
char commit_str[] = "commit transaction";
//Zapis do bazy
if (option == 2) {
//Odczyt obrazka z pliku
CImg<unsigned char> image(file_i);
//Poczatek tranzakcji
sqlite3_exec (db, begin_str, NULL, NULL, NULL);
sqlite3_prepare(db, insert_str, -1, &insert_stmt, NULL);
//Wstawienie danych do tabeli
sqlite3_bind_blob(insert_stmt, 1, image.ptr(), image.size() * sizeof(unsigned char), SQLITE_STATIC);
sqlite3_bind_text(insert_stmt, 2, file_i, strlen(file_i)* sizeof(unsigned char), SQLITE_STATIC);
sqlite3_bind_int (insert_stmt, 3, image.dimx());
sqlite3_bind_int (insert_stmt, 4, image.dimy());
sqlite3_bind_int (insert_stmt, 5, image.dimz());
sqlite3_bind_int (insert_stmt, 6, image.dimv());
sqlite3_step (insert_stmt);
sqlite3_reset(insert_stmt);
//Zakończenie tranzakcji
sqlite3_exec(db, commit_str, NULL, NULL, NULL);
}
else { //option==1
//Odczyt danych z tabeli
sqlite3_prepare(db, select_str,-1, &select_stmt, NULL);
while (sqlite3_step(select_stmt) == SQLITE_ROW) {
//odczytujemy nazwe pliku
filename = sqlite3_column_text(select_stmt, 2);
//odczytujemy rozmiary obrazu
dx = sqlite3_column_int(select_stmt, 3);
dy = sqlite3_column_int(select_stmt, 4);
dz = sqlite3_column_int(select_stmt, 5);
dv = sqlite3_column_int(select_stmt, 6);
//pobieramy obraz
int num_bytes = sqlite3_column_bytes(select_stmt,1);
num_bytes = num_bytes / sizeof(unsigned char);
data_buffer = new unsigned char[num_bytes];
data_buffer = (const unsigned char*)sqlite3_column_blob(select_stmt, 1);
printf("%s (%d,%d,%d,%d)\n",filename,dx,dy,dz,dv);
//tworzymy i wyświetlamy obraz
CImg<unsigned char> tmp(data_buffer,dx,dy,dz,dv);
CImgDisplay main_img(tmp, "Obraz wejsciowy");
while (!main_img.is_closed);
}
}
sqlite3_close(db);
return 0;
}Podczas kompilacji poza standardowymi bibliotekami wymaganymi przez CImg dołączamy również bibliotekę sqlite.
g++ imagedb1.cpp -o imagedb1 -lX11 -lpthread -lsqlite3
Przykładowe wywołanie programu dodające 2 obrazki do bazy:
./imagedb1 -d image_db.sqlite3 -o 2 -i input1.bmp ./imagedb1 -d image_db.sqlite3 -o 2 -i input2.bmp
Wyświetlenie obrazów z bazy:
./imagedb1 -d image_db.sqlite3
Analizując wyżej przedstawiony przykład łatwo można dostrzec zalety korzystania z baz danych w aplikacjach służących do przetwarzania obrazu. Najważniejszą z nich jest możliwość zapisywania stanu aplikacji i kontynuowania pracy po jego kolejnym uruchomieniu.
