ps:这是以前做过的东西,理解不够深,简单说一下方法作为参考
首先学习一下怎么用c++对mysql数据库进行操作,通常我们可以用odbc或调用mysql的api来操作数据库。虽然后来觉得用odbc比较好,但最初使用的方法是调用mysql的api方法也就没再作修改。
c++调用mysqlapi的配置及使用方法可参考:http://blog.csdn.net/leixiaohua1020/article/details/12753367
按照链接中的方法粗略了解了怎么用c++调用mysql的api后,接下来考虑的是如何把图片存入数据库中。
数据库中可以使用BLOB(binary large object)数据类型来存储图片。
BLOB是数据库中用来存储二进制对象的类型,这里我们可以用BLOB,MEDIUMBLOB和LONGBLOB,存储的二进制对象大小分别为64K,16M,4G。
知道了数据库中可以用什么类型存储图片之后我们就可以尝试把图片存入数据库了
具体代码可以参考:http://www.cnblogs.com/sherlockhua/archive/2012/03/31/2426399.html 中的八、九(建议新手从头看)
以上是准备工作。。。
因为程序是用opencv写的,需要把opencv读取后的图片(这里用的是c++中的cv::Mat类型)存到数据库中,所以在这里需要自己写一个把cv::Mat类型存到数据库中的方法。为了能够把存入的图片数据恢复成cv::Mat我们要存的就不仅仅是二进制数据了,还需要存入一些其他信息。
在mysql中建立用来存储图像的表(时间过长忘记留图了 ==! ,画一个示例):
id|rows |cols| type | data
=======================
其中,id是用来查询用的。rows和cols用来存储图像的高度和宽度。type用来存储图像像素点的数据类型。data用来存储图像的灰度信息。
建完表之后我们就可以在c++中调用mysql的api来操作了,直接贴代码了。。。。。
所用的操作封装到了ImgDB类中下面是类声明的头文件。
<span style="font-size:14px;"><span style="font-size:18px;">#pragma once
#include <cv.h>
#include <highgui.h>
#include <Windows.h>
#include <mysql.h>
#include <vector>
class ImgDB
{
public:
ImgDB(char * host,char * user,char * psw,char * db,int port);
~ImgDB(void);
private:
MYSQL *con;
public:
int saveone(char *table, int id, cv::Mat & image);<span style="white-space:pre"> </span>//保存一张图片
int loadall(char * table, std::vector<int> & labels, std::vector<cv::Mat> & images);<span style="white-space:pre"> </span>//载入数据库所有图片
int delbyid(char * table, int id);<span style="white-space:pre"> </span>//按id删除图片
};
</span></span>
下面是ImgDB类的初始化与销毁,在构造函数中创建了一个mysql的连接,在析构中释放了这个连接。<span style="font-size:14px;"><span style="font-size:18px;">ImgDB::ImgDB(char * host,char * user,char * psw,char * db,int port)
: con(NULL)
{
con = mysql_init(NULL);
mysql_real_connect(con,host,user,psw,db,port,NULL,0);
}
ImgDB::~ImgDB(void)
{
mysql_close(con);
}</span></span>
向数据库中保存一张图片的。<span style="font-size:14px;"><span style="font-size:18px;">int ImgDB::saveone(char *table, int id, cv::Mat & image)
{
MYSQL_STMT * stmt;
MYSQL_BIND bind[5];
int rows = image.rows;
int cols = image.cols;
int type = image.type();
int channels = image.channels();
uchar * data = image.data;
stmt = mysql_stmt_init(con);
char sql[400];
sprintf(sql,"insert into %s values(?,?,?,?,?)",table);
mysql_stmt_prepare(stmt, sql, strlen(sql));
fprintf(stdout, " total parameters in INSERT: %d\n", mysql_stmt_param_count(stmt));
unsigned long len;
memset(bind,0,sizeof(bind));
bind[0].buffer_type = MYSQL_TYPE_LONG;
bind[0].buffer = &id;
bind[0].is_null = 0;
len = sizeof(int);
bind[0].length = &len;
bind[1].buffer_type = MYSQL_TYPE_LONG;
bind[1].buffer = &rows;
bind[1].is_null = 0;
len = sizeof(int);
bind[1].length = &len;
bind[2].buffer_type = MYSQL_TYPE_LONG;
bind[2].buffer = &cols;
bind[2].is_null = 0;
len = sizeof(int);
bind[2].length = &len;
bind[3].buffer_type = MYSQL_TYPE_LONG;
bind[3].buffer = &type;
bind[3].is_null = 0;
len = sizeof(int);
bind[3].length = &len;
bind[4].buffer_type = MYSQL_TYPE_MEDIUM_BLOB;
bind[4].buffer = data;
bind[4].is_null = 0;
len = cols*rows*channels;
bind[4].length = &len;
mysql_stmt_bind_param(stmt, bind);
mysql_stmt_execute(stmt);
fprintf(stdout, " total affected rows(insert 1): %lu\n",(unsigned long) mysql_stmt_affected_rows(stmt));
mysql_stmt_close(stmt);
return 0;
}</span></span>
读取数据库表中所有的图片。<span style="font-size:14px;"><span style="font-size:18px;">int ImgDB::loadall(char * table, std::vector<int> & labels, std::vector<cv::Mat> & images)
{
char sql[400];
sprintf(sql,"select * from %s",table);
int rt=mysql_real_query(con,sql,strlen(sql));
if(rt)
{
printf("Error making query!!!\n");
}
else
{
printf("%s executed!!!\n",sql);
}
MYSQL_RES * res = mysql_store_result(con);//将结果保存在res结构体中
MYSQL_ROW row;
int id;
int rows;
int cols;
int type;
uchar * data;
int count = 0;
while(row=mysql_fetch_row(res))
{
//MYSQL_ROW STDCALL mysql_fetch_row(MYSQL_RES *result);
//检索行
id = atoi(row[0]);
printf("%d\n",id);
rows = atoi(row[1]);
printf("%d\n",rows);
cols = atoi(row[2]);
printf("%d\n",cols);
type = atoi(row[3]);
printf("%d\n",type);
data = (uchar *)row[4];
printf("................\n");
cv::Mat image(rows,cols,type,data);
cv::namedWindow("FetchedImage");
cv::imshow("FetchedImage",image);
cv::waitKey(0);
cv::destroyWindow("FetchedImage");
labels.push_back(id);
images.push_back(image);
count++;
}
std::vector<int>::iterator first = labels.begin();
std::vector<int>::iterator last = labels.end();
printf("number of rows %d\n",count);
printf("mysql_free_result...\n");
mysql_free_result(res);
return 0;
}</span></span>
按照id来删除一组图片(由于id不是主键不唯一)。<span style="font-size:14px;"><span style="font-size:18px;">int ImgDB::delbyid(char * table, int id)
{
MYSQL_STMT * stmt;
MYSQL_BIND bind[1];
stmt = mysql_stmt_init(con);
char sql[400];
sprintf(sql,"delete from %s where id = ?",table);
mysql_stmt_prepare(stmt, sql, strlen(sql));
fprintf(stdout, " total parameters in INSERT: %d\n", mysql_stmt_param_count(stmt));
unsigned long len;
memset(bind,0,sizeof(bind));
bind[0].buffer_type = MYSQL_TYPE_LONG;
bind[0].buffer = &id;
bind[0].is_null = 0;
len = sizeof(int);
bind[0].length = &len;
mysql_stmt_bind_param(stmt, bind);
mysql_stmt_execute(stmt);
fprintf(stdout, " total affected rows(insert 1): %lu\n",(unsigned long) mysql_stmt_affected_rows(stmt));
mysql_stmt_close(stmt);
return 0;
}
</span></span>
下面的代码是调用ImgDB类中的方法实现图片的图片的读取。这里把读出来的图片转成容器。代码中注释掉的部分分别是存入和删除。以下仅是测试。
<span style="font-size:14px;"><span style="font-size:18px;">// ImgDBtest.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <cv.h>
#include <highgui.h>
#include "ImgDB.h"
#include <vector>
int _tmain(int argc, _TCHAR* argv[])
{
char * filename = "4.jpg";
cv::Mat image = cv::imread(filename);
char * host = "localhost";
char * user = "root";
char * psw = "321654";
char * db = "mydb";
int port = 3306;
char * table = "imgdata";
ImgDB imgdb(host,user,psw,db,port);
//imgdb.saveone(table,0,image);
std::vector<int> labels;
std::vector<cv::Mat> images;
imgdb.loadall(table,labels,images);
std::vector<int>::iterator first = labels.begin();
std::vector<int>::iterator last = labels.end();
std::cout<<labels.size()<<"||"<<images.size()<<std::endl;
while(first != last)
{
std::cout<<*first;
first++;
}
//imgdb.delbyid(table,0);
getchar();
return 0;
}
</span></span>
注:1、代码中使用的是调用mysql的预处理语句并没有使用链接中提供的方法而是用传参的方式调用sql语句,也可以使用mysql_real_escape_string(conn, chunk, data, size);语句先转义字符。这两种方法都可以避免sql注入。关于sql注入网上有很多说明。
2、由于图像灰度数据中也可能存在值为‘\0’的数据,如果单纯用len()方法来计算图像数据长度会导致我们存入的数据长度与实际存入图片的长度不符。例如:之前我使用的图片是600×900的三通道图片,存入的数据长度为17000多。通过计算我们可以得到图像的数据长度应该是600×900×3。所以这样的数据在读出来构造cv::Mat时会产生异常。所以我们自己计算图像数据的实际长度。