2016年11月23日 星期三

C# + MongoDB 簡易型函式庫


    MongoDB的操作語法有別於實體資料庫,雖然支援LINQ,但新增/刪除/修改等方式仍然要以MongoDB Driver 的語法為主,所以不熟悉MongoDB的朋友們超級的痛苦,所以自製簡易版的函式庫,供大家參考囉~ 也歡迎大家多多交流。

public class MongoDBComponent
{
string _connString;
MongoClient _client;
IMongoDatabase _server;
public MongoDBComponent() {
  try
  {
    _connString = "mongodb://localhost:27017"; //未來要改為設定檔的方式,目前暫時以本機端的mongoDB為測試
    _client = new MongoClient(_connString);
    _server = _client.GetDatabase("mydb");
  }
  catch (Exception ex)
  {
    throw new Exception(ex.Message);
  }
 }
}

PS:注意事項
作者以本機端的MongoDB為測試機,記得要改_connString的設定值喔,另外要改為讀取設定值比較好維護 … 當然前提要安裝mongoDB Driver for c#

一、新增
public bool Add<T>(string tableName, T value )
{
  try
  {
    string jsonString = Newtonsoft.Json.JsonConvert.SerializeObject(value);
    BsonDocument document= BsonSerializer.Deserialize<BsonDocument>(jsonString);
    var collection = _server.GetCollection<BsonDocument>(tableName);
    collection.InsertOne(document);
    return true;
  }
  catch (Exception ex)
  {
     throw new Exception(ex.Message); 
  }
}
MongoDB是支援JSON格式,所有的物件經過JSON格式化後成為字串格式,才經由BsonDocument 轉成物件後,就可以傳入資料給MongoDB

二、修改
public bool Update<T>(string tableName,string searchKey, object searchValue, T value )
{
try
  {
   var collection = _server.GetCollection<BsonDocument>(tableName);
   var filter = Builders<BsonDocument>.Filter.Eq(searchKey, searchValue);
   var atts = value.GetType().GetProperties();

   //取得所有元素
   string attributeName = "";
   Type typeObj;
   PropertyInfo properInfo;
   object attributeValue;
   for (int i = 0; i < atts.Length - 1; i++)
   {
    attributeName = atts[i].Name.ToString();
    typeObj = value.GetType();
    properInfo = typeObj.GetProperty(attributeName);
    attributeValue = properInfo.GetValue(value);
    if (attributeValue == null || attributeName== searchKey)
    {
        continue;
    }
    var update = Builders<BsonDocument>.Update.Set(attributeName, attributeValue);
    var result = collection.UpdateOne(filter, update);
    }
    return true;
  }
  catch (Exception ex)
  {
    throw new Exception(ex.Message);
  }
}



MongoDB更新的方式要呼.update.set(''欄位名稱'',更新值), 但如果有多筆欄位值更新時,就.update.set(''欄位名稱1'',更新值).set(''欄位名稱2'',更新值) 一直串的set 串接在一起,但是想改為共用的原件,就只好用笨方法,找到物件的所有屬性後,逐一跑迴圈執行Update.Set 每個欄位就執行一次Update , 如果各位有更好的方法歡迎提供…

三、刪除
public bool Delete<T>(string tableName, string searchKey, object searchValue)
{
  try
  {
      var collection = _server.GetCollection<T>(tableName);
      var filter = Builders<T>.Filter.Eq(searchKey, searchValue);
      var result = collection.DeleteOne(filter);
      return true;
  }
  catch (Exception ex)
  {
      throw new Exception(ex.Message);
  }
}
刪除資料時,要先查詢出要刪除的那筆資料,所以呼叫Filter.Eq 找到那筆資料後,再呼叫DeleteOne ,如果說要刪除多筆時,MongoDB 有另外的函式呼叫多筆刪除喔,可別傻傻的一直呼叫DeleteOne, 這邊就不再做介紹,請有興趣的客倌去goole!

四、查詢資料
public IEnumerable<T> FindMany<T>(string tableName,Expression<Func<T,bool>>whereConditions)
{
  try
  {
    var collection = _server.GetCollection<T>(tableName).AsQueryable<T>();
    var result= collection.Where(whereConditions).AsQueryable();
    return result;
  }
  catch (Exception ex)
  {
    throw new Exception(ex.Message);
  }
}

查詢資料時為了可以自訂查詢條件,所以用 Expression<Func<T,bool>>whereConditions,允許傳入linqlamda 查詢條件,更具彈性,並將MongoDB的查詢資料列直接轉成Ienumerable<T>

五、練習 MongoDBComponent
public class Member :IEntityDB<Member>
{
[BsonId]
public ObjectId _id { get; set; }
public string Account { get; set; }
public string IdentityNumber { get; set; }
public string Password { get; set; }
public string Name { get; set; }
public string Mobile { get; set; }
public string Tel { get; set; }
public string Address { get; set; }
public string Email { get; set; }
public DateTime CreateDate { get; set; }
private MongoDBComponent _MongoDBEntity = new MongoDBComponent();

public bool Add(Member entityMember)
{
    return _MongoDBEntity.Add<Member>("Member", entityMember);
}

public bool Delete(Member entityMember)
{
    return _MongoDBEntity.Delete<Member>("Member", "_id", entityMember._id);
}

public bool Update(Member entityMember)
{
    return _MongoDBEntity.Update<Member>("Member", "_id", entityMember._id, entityMember);
}

public Member FindOne(Member entityMember)
{
    return _MongoDBEntity.FindMany<Member>("Member", q => q._id == entityMember._id).FirstOrDefault();
}

public IEnumerable<Member> FindMany(Expression<Func<Member, bool>> whereConditions)
{
    return _MongoDBEntity.FindMany<Member>("Member", whereConditions);
 }
}
這樣c#就能保持使用entity Framework的習慣和彈性,具有基本新增/刪除/修改/查詢,如果有需要擴充其他方式,還請各位自已修改囉!

2016年11月11日 星期五

如何用Node.js 人臉偵測

        OpenCV的功能非常強大,所以它被其他程式引用,所以node.jsopenCV的函式庫引用進來,所以透過前端網頁做人臉偵測,前端的技術又向前邁進了一大步囉~ 但是前置作業還挺麻煩,就簡單教大家如何從無到有做出人臉偵測的效果吧!

一、安裝OPENCV套件

1. 要先下戴OpenCVwindows 安裝程式
選擇你喜歡的路徑 解壓縮就可以囉~


2 設定環境變數: OPENCV_DIR: OPENCV的路徑\build\x64\vc12
PS注意: 如果是32位元的電腦 要使用 \build\x86\vc12

2 設定path變數: %OPENCV_DIR%\bin
這樣OPENCV就安裝完畢了!!!

二、OpenCV 偵測
編寫pack.json
{
"name": "edi-cam",
"private": true,
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "4.10.1",
"morgan": "1.4.1",
"opencv": "git+https://github.com/peterbraden/node-opencv.git",
"socket.io": "1.2.1"
}
}

如果有相關的套件,可以一次編寫下戴的工具包和版本,就會全自動下戴完成囉~

注意事項: 如果有出現紅字就檢查是否有【OPENCV_DEV】沒有設定,或者用錯X86/X64的資料夾,都會出現下戴opencv 失敗

建立serve\config\defaultPath.js 設定HTML網頁路徑
var path = require('path') //路徑模組
module.exports = {
httpPort: 8080,
staticFolder: path.join(__dirname + '/../../client') //預設瀏覽網頁的放置路徑
};

建立serve\config\defautlPage.js 設定HTML網頁名稱
exports.serveIndex = function (app, staticFolder) {
app.get('*', function (req, res) {
res.sendFile('index.html', { root: staticFolder });
});
};

建立 server\config\faceDetect.js 偵測人臉位置
var cv = require('opencv'); //使用openCV模組
var fs=require('fs');
var camWidth = 320;
var camHeight = 240;
var camFps = 10;
var camInterval = 1000 / camFps; //偵測間隔

// 設定外框的顏色
var rectColor = [0, 255, 0];
var rectThickness = 2; // 設定外框的厚度
var filename="./node_modules/opencv/tmp/image.png"; //儲存檔案名稱
// initialize camera
var camera = new cv.VideoCapture(0); //取得視訊串流
camera.setWidth(camWidth);
camera.setHeight(camHeight);

module.exports = function (socket) {
setInterval(function() {
camera.read(function(err, im) {
if (err) throw err;
//偵測臉部用的XML
im.detectObject('./node_modules/opencv/data/haarcascade_frontalface_alt2.xml', {}, function(err, faces) {
if (err) console.log(err);
for (var i = 0; i < faces.length; i++) {
face = faces[i];
//在臉的位置加四方型的框,自訂 RectColor 顏色, 自訂rectThickness 框寬
im.rectangle([face.x, face.y], [face.width, face.height], rectColor, rectThickness);
}
im.save("./node_modules/../../client/image.png"); //儲存圖片
fs.readFile("./node_modules/../../client/image.png", function (err, data) {
//讀取圖片檔案
if (err) throw err;
//觸發frame 事件時,傳回圖片串流 (base64)
socket.emit('frame', { buffer: new Buffer(data).toString('base64') });
});
});
});
}, camInterval);
};

建立 server\server.js 呼叫socket.io 架伺服器

// modules
var express = require('express')
, http = require('http')
, morgan = require('morgan');

//取得網頁檔案路徑的設定
var configServer = require('./config/pathDirectory');

// app parameters
var app = express();
app.set('port', configServer.httpPort); //取得網頁的port
app.use(express.static(configServer.staticFolder)); //取得網頁的路徑
app.use(morgan('dev'));//記錄日誌檔

//呼叫 defautlPage serveIndex 函數
require('./config/defaultPage').serveIndex(app, configServer.staticFolder);

//呼叫 http 建立server
var server = http.createServer(app);
server.listen(app.get('port'), function () {
console.log('HTTP server listening on port ' + app.get('port'));
});

//呼叫soket.io 模組 ,啟動人臉偵測
var io = require('socket.io')(server);
io.on('connection', require('./config/faceDetect'));

module.exports.app = app;

建立 client\app.js
var socket = io.connect('http://localhost');
var canvas = document.getElementById('canvas-video'); //取得HTMLHTMLcanvas ID
var context = canvas.getContext('2d'); //初始化畫布
var img = new Image(); //宣告圖片
context.fillStyle = '#333';
context.fillText('Loading...', canvas.width/2-30, canvas.height/3); //繪製文字

//Client連到server端,server端會觸發frame事件
socket.on('frame', function (data) {
img.onload = function () {
context.drawImage(this, 0, 0, canvas.width, canvas.height);
};
img.src = 'data:image/png;base64,' + data.buffer; //取回圖片串流
});

建立 client\index.html
<html>
<head>
<meta name="viewport" content="initial-scale=1"/>
<title>face-detection-node-opencv</title>
<link rel="stylesheet" type="text/css" href="/styles.css">
</head>
<body>
<div class="container center">
<canvas id="canvas-video" width="640" height="480"></canvas>
</div>
<script src="/socket.io/socket.io.js"></script> //引用socket.io-client 目錄下的socket.io.js
<script src="app.js"></script>
</body>
</html>

PS: 原本soket.io.js的路徑放置在 \node_modules\socket.io\node_modules\socket.io-client socket.io.js 拷貝到 \node_modules\socket.io\ 目錄下

建立 client\style.css

.container {
padding-top: 50px;
}

.center {
text-align: center;
}

#canvas-video {
width: 640px;
height: 480px;
border: 1px solid #ccc;
}

範例:

OpenCV除了人臉偵測外,還有其他的模組偵測比如眼晴,此外OPENCV 可以繪製圖框在影像上,不就如外面的AR的虛擬實境了嗎? 當然OPCN最強大的還是偵測物品的功能啦~
感謝大家的拜訪… 也歡迎大家留言多多討論囉!!!


三、參考文獻