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.

0 komentarze:
Prześlij komentarz