一個帶人臉識別的智能照相機應(yīng)用

語音識別,語義理解一站式解決之智能照相機(人臉識別,olami)

轉(zhuǎn)載請注明CSDN博文地址:http://blog.csdn.net/ls0609/article/details/76546716

olami sdk實現(xiàn)了把錄音或者文字轉(zhuǎn)化為用戶可以理解的json字符串從而實現(xiàn)語義理解疗杉,用戶可以定義自己的

語義吨掌,通過這種方式可以實現(xiàn)用戶需要的語義理解。前面寫了兩篇語音識別劳淆,語義理解的博文,分別是語音

在線聽書和語音記帳軟件,本篇是語音智能照相機端仰。

1.智能照相機的功能

手機后攝像頭像素比較高,如果用后設(shè)想頭對準(zhǔn)自己自拍残家,那么看不到屏幕的情況下怎么知道

自己在不在鏡頭中呢榆俺?而本篇做的智能照相機就可以為您解決這個問題。

想要做的是這樣一個照相機app坞淮,可以語音切換攝像頭茴晋,人臉識別并語音播報識別的人臉是否在屏幕中央,

是偏向哪里回窘,當(dāng)人臉居中的時候诺擅,提示用戶可以拍照了,用戶說“拍照”啡直,“茄子”就會自動抓拍并保存圖

片在手機中烁涌,還可以說“切換攝像頭”來切換前后攝像頭。

抓了兩張應(yīng)用運行時的圖片:




2.eclipse中的lib目錄結(jié)構(gòu)如下


assets下面的事tts播報的資源文件

libs目錄下酒觅,

libtts.so tts播報所需的庫文件

libspeex.so語音識別所需的庫文件

libolamsc.so語音識別所需的庫文件

tts.jar tts播報所需的庫文件

voicesdk_android.jar語音識別所需的庫文件

3.AndroidManifest.xml


需要錄音撮执,網(wǎng)絡(luò),讀寫sd卡舷丹,拍照等權(quán)限抒钱。

4.layout布局

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent">

android:layout_width="match_parent"

android:layout_height="match_parent">

android:layout_width="match_parent"

android:layout_height="wrap_content"/>

android:id="@+id/faceView"

android:layout_width="match_parent"

android:layout_height="match_parent"/>

android:id="@+id/btn_start"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignParentBottom="true"

android:layout_centerHorizontal="true"

android:text="開始"/>

在surfaceview中自定義了一個FaceView,faceview用來顯示抓拍的人臉。

屏幕最下方有個button谋币,因為這個版本暫時不支持語音喚醒功能(后續(xù)添加后再更新)仗扬,添加一個button用于用戶想隨時說拍照的時候點擊觸發(fā)用。

5.MainActivity.java和FaceView.java

-1.MainActivity.Java

@Override

protectedvoidonCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.layout_camera);

initHandler();//用于處理錄音狀態(tài)回調(diào)的消息

initView();//初始化界面

initViaVoiceRecognizerListener();//初始化olami語音回調(diào)監(jiān)聽

init();//初始化olami語音識別sdk

initTts();//初始化tts語音播報

DisplayMetrics dm =newDisplayMetrics();//定義DisplayMetrics對象

getWindowManager().getDefaultDisplay().getMetrics(dm);//取得窗口屬性

mScreenCenterx = dm.widthPixels/2;//窗口的寬度

mScreenCentery = dm.heightPixels/2;//窗口的高度

}

以下是olamisdk的初始化

publicvoidinit()

{

mOlamiVoiceRecognizer =newOlamiVoiceRecognizer(MainActivity.this);

TelephonyManager telephonyManager=

(TelephonyManager)this.getSystemService(

this.getBaseContext().TELEPHONY_SERVICE);

Stringimei=telephonyManager.getDeviceId();

mOlamiVoiceRecognizer.init(imei);//設(shè)置身份標(biāo)識蕾额,可以填null

//設(shè)置識別結(jié)果回調(diào)listener

mOlamiVoiceRecognizer.setListener(mOlamiVoiceRecognizerListener);

//設(shè)置支持的語音類型早芭,優(yōu)先選擇中文簡體

mOlamiVoiceRecognizer.setLocalization(

OlamiVoiceRecognizer.LANGUAGE_SIMPLIFIED_CHINESE);

mOlamiVoiceRecognizer.setAuthorization(

"51a4bb56ba954655a4fc834bfdc46af1",

"asr",

"68bff251789b426896e70e888f919a6d",

"nli");

//注冊Appkey,在olami官網(wǎng)注冊應(yīng)用后生成的appkey

//注冊api诅蝶,請直接填寫“asr”退个,標(biāo)識語音識別類型

//注冊secret,在olami官網(wǎng)注冊應(yīng)用后生成的secret

//注冊seq秤涩,請?zhí)顚憽皀li”

//錄音時尾音結(jié)束時間帜乞,建議填//2000ms

mOlamiVoiceRecognizer.setVADTailTimeout(2000);

//設(shè)置經(jīng)緯度信息,不愿上傳位置信息筐眷,可以填0

mOlamiVoiceRecognizer.setLatitudeAndLongitude(

31.155364678184498,121.34882432933009);

}

定義OlamiVoiceRecognizerListener黎烈,此處代碼就不貼了。

onError(int errCode)//出錯回調(diào)匀谣,可以對比官方文檔錯誤碼看是什么錯誤

onEndOfSpeech()//錄音結(jié)束

onBeginningOfSpeech()//錄音開始

onResult(String result, int type)//result是識別結(jié)果JSON字符串

onCancel()//取消識別照棋,不會再返回識別結(jié)果

onUpdateVolume(int volume)//錄音時的音量,1-12個級別大小音量

以下是handler消息處理武翎,包含語義解析

privatevoidinitHandler()

{

mHandler =newHandler(){

@Override

publicvoidhandleMessage(Message msg)

{

switch(msg.what){

caseMessageConst.CLIENT_ACTION_START_RECORED:

mBtnStart.setText("錄音中");

break;

caseMessageConst.CLIENT_ACTION_STOP_RECORED:

mBtnStart.setText("識別中");

break;

caseMessageConst.CLIENT_ACTION_CANCEL_RECORED:

mBtnStart.setText("開始");

break;

caseMessageConst.CLIENT_ACTION_ON_ERROR:

mBtnStart.setText("開始");

break;

caseMessageConst.CLIENT_ACTION_UPDATA_VOLUME:

//mTextViewVolume.setText("音量: "+msg.arg1);

break;

caseMessageConst.SERVER_ACTION_RETURN_RESULT:

mBtnStart.setText("開始");

try{

String message =(String) msg.obj;

String input =null;

JSONObject jsonObject =newJSONObject(message);

JSONArray jArrayNli =

jsonObject.optJSONObject("data").optJSONArray("nli");

JSONObject jObj =jArrayNli.optJSONObject(0);

JSONArrayjArraySemantic =null;

if(message.contains("semantic"))

{

jArraySemantic =jObj.getJSONArray("semantic");

String modifier =

jArraySemantic.optJSONObject(0).optJSONArray(

"modifier").optString(0);

if("take_photo".equals(modifier))

capture();

elseif("switch_camera".equals(modifier))

switchCamera();

}

else{

Log.i("ppp","result

error");

}

}

catch(Exception e)

{

e.printStackTrace();

}

break;

caseMessageConst.CLIENT_ACTION_UPDATA_FACEDECTION_DATA:

if(mIsRecording)

break;

RectF rect = (RectF) msg.obj;

mLeft = rect.left;

mRight = rect.right;

mTop = rect.top;

mBottom = rect.bottom;

floatcenterx = mLeft +(mRight - mLeft)/2;

floatcentery = mTop + (mBottom-mTop)/2;

String promptString ="";

if(centerx

Math.abs(mScreenCenterx-centerx) >100)

promptString ="位置偏左,";

elseif((centerx > mScreenCenterx)&&

(Math.abs(centerx -mScreenCenterx)>100))

promptString ="位置偏右,";

if((centery

Math.abs(mScreenCentery-centery) >200))

{

if("".equals(promptString))

promptString ="位置偏上";

else

promptString +="并且偏上";

}

elseif((centery > mScreenCentery)&&

(Math.abs(centery-mScreenCenterx)>200))

{

if("".equals(promptString))

promptString ="位置偏下";

else

promptString +="并且偏下";

}

if("".equals(promptString))

{

promptString ="位置已經(jīng)居中,可以拍照了";

mIsCenter =true;

}

else

{

mIsCenter =false;

}

ITtsListener ttsListener =newITtsListener()

{

@Override

publicvoidonPlayEnd() {

if(mIsCenter)

{

if(mOlamiVoiceRecognizer

!=null)

mOlamiVoiceRecognizer.start();

}

}

@Override

publicvoidonPlayFlagEnd(String arg0) {

}

@Override

publicvoidonTTSPower(longarg0) {

}

};

TtsPlayer.playText(MainActivity.this,

promptString, ttsListener,Tts.TTS_SYSTEM_PRIORITY);

break;

}

}

};

}

在MessageConst.SERVER_ACTION_RETURN_RESULT消息中烈炭,通過解析服務(wù)器返回的json字符串,可以找到modifier這個字段的值宝恶,如果是take_photo表示拍照符隙,如果是switch_camera表示切換攝像頭。

當(dāng)用戶說拍照或者茄子的時候垫毙,服務(wù)器返回如下json字符串:

[

{

"desc_obj": {

"status":0

},

"semantic": [

{

"app":"camera",

"input":"拍照",

"slots": [

],

"modifier": [

"take_photo"

],

"customer":"58df512384ae11f0bb7b487e"

}

],

"type":"camera"

}

]

這個拍照霹疫,茄子等語法都是自己定義的,詳細(xì)請看:

olami開放平臺語法編寫簡介:http://blog.csdn.net/ls0609/article/details/71624340

olami開放平臺語法官方介紹:https://cn.olami.ai/wiki/?mp=nli&content=nli2.html

2.人臉識別FaceView.java

publicclassFaceViewextendsView{

privateCamera.Face[] mFaces;

privatePaint mPaint;

privateMatrix matrix =newMatrix();

privateRectF mRectF =newRectF();

privateHandler mHandler;

privatelongmCurrentTime;

publicvoidsetFaces(Camera.Face[] faces) {

mFaces = faces;

invalidate();

}

publicFaceView(Context context) {

super(context);

init(context);

}

publicFaceView(Context context, AttributeSet attrs) {

super(context, attrs);

init(context);

}

publicFaceView(Context context, AttributeSet attrs,intdefStyleAttr) {

super(context, attrs, defStyleAttr);

init(context);

}

publicvoidinit(Context context) {

mPaint =newPaint();

mPaint.setColor(Color.RED);

mPaint.setStrokeWidth(5f);

mPaint.setStyle(Paint.Style.STROKE);

}

publicvoidsetHandler(Handler handler)

{

mHandler = handler;

}

@Override

protectedvoidonDraw(Canvas canvas) {

super.onDraw(canvas);

if(mFaces ==null|| mFaces.length <0) {

return;

}

//準(zhǔn)備矩形框

MainActivity.prepareMatrix(matrix,false,270, getWidth(),getHeight());

canvas.save();

matrix.postRotate(0);

canvas.rotate(-0);

RectF tempRectF =newRectF();

longtempTime = System.currentTimeMillis();

for(inti =0; i < mFaces.length; i++) {

mRectF.set(mFaces[i].rect);//獲取face矩形框值

floattemp = mRectF.top;

mRectF.top = -mRectF.bottom;

mRectF.bottom = - temp;//上下交換

matrix.mapRect(mRectF);

canvas.drawRect(mRectF, mPaint);//繪制矩形框

tempRectF.set(mRectF);

if((mCurrentTime ==0)

||((tempTime-mCurrentTime)/1000) >=4)

{//超過4秒综芥,發(fā)送一次識別face矩形框值

mHandler.sendMessage(mHandler.obtainMessage(

MessageConst.CLIENT_ACTION_UPDATA_FACEDECTION_DATA, tempRectF));

mCurrentTime = tempTime;

}

Log.i("ppp","mRectF.left = "+mRectF.left+"mRectF.right = "+mRectF.right);

}

canvas.restore();

}

}

自定義FaceView中丽蝎,由于旋轉(zhuǎn)了270度,所以需要face矩形框上下值進(jìn)行交換膀藐,不然人臉識別總是左右或者上下不能追蹤屠阻。每隔4秒發(fā)送一次矩形框的值,在MainActivity.java的handler中收到這個消息并進(jìn)行是否居中的判斷额各。

caseMessageConst.CLIENT_ACTION_UPDATA_FACEDECTION_DATA:

if(mIsRecording)

break;

RectF rect = (RectF) msg.obj;

mLeft = rect.left;

mRight = rect.right;

mTop = rect.top;

mBottom = rect.bottom;//保存上下左右的矩形框值

floatcenterx = mLeft +(mRight - mLeft)/2;//獲取矩形框橫向中心點位置

floatcentery = mTop

+ (mBottom-mTop)/2;//獲取矩形框縱向中心點位置

String promptString ="";

if(centerx

>100)

promptString ="位置偏左,";

elseif((centerx> mScreenCenterx)&&

(Math.abs(centerx-mScreenCenterx)>100))

promptString ="位置偏右,";

if((centery < mScreenCentery)&&(

Math.abs(mScreenCentery-centery) >200))

{

if("".equals(promptString))

promptString ="位置偏上";

else

promptString +="并且偏上";

}

elseif((centery> mScreenCentery)&&

(Math.abs(centery -mScreenCenterx)>200))

{

if("".equals(promptString))

promptString ="位置偏下";

else

promptString +="并且偏下";

}

if("".equals(promptString))

{

promptString ="位置已經(jīng)居中,可以拍照了";

mIsCenter =true;

}

else

{

mIsCenter =false;

}

ITtsListener ttsListener =newITtsListener()

{

@Override

publicvoidonPlayEnd() {

if(mIsCenter)

{

if(mOlamiVoiceRecognizer !=null)

mOlamiVoiceRecognizer.start();

}

}

@Override

publicvoidonPlayFlagEnd(String arg0) {

}

@Override

publicvoidonTTSPower(longarg0) {

}

};

TtsPlayer.playText(MainActivity.this,

promptString,ttsListener,Tts.TTS_SYSTEM_PRIORITY);

break;

可以獲得屏幕的中心點和人臉識別的矩形框的中心點国觉,對比橫向和縱向的中心點大小和絕對值差,當(dāng)橫向的值差100像素以上就認(rèn)為橫向不居中虾啦,并且根據(jù)大小分居左和居右蛉加,縱向大小差值在200像素以上認(rèn)為縱向不居中蚜枢,并且根據(jù)大小分偏上和偏下缸逃,這個100针饥,200像素值用戶可以自己調(diào)節(jié)到合適的值。

調(diào)用TtsPlayer.playText提示需频,當(dāng)播報結(jié)束后回調(diào)到onPlayEnd()丁眼,如果居中那么已經(jīng)提示用戶可以拍照了,此時啟動錄音程序昭殉,用戶不用點擊button也不用喚醒苞七,只許說拍照或者茄子就可以拍照了。

6.源碼下載鏈接

https://pan.baidu.com/s/1qXITWs8

7.相關(guān)鏈接

語音在線聽書:http://blog.csdn.net/ls0609/article/details/71519203

語音記賬demo:http://blog.csdn.net/ls0609/article/details/72765789

olami開放平臺語法編寫簡介:http://blog.csdn.net/ls0609/article/details/71624340

olami開放平臺語法官方介紹:https://cn.olami.ai/wiki/?mp=nli&content=nli2.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末挪丢,一起剝皮案震驚了整個濱河市蹂风,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌乾蓬,老刑警劉巖惠啄,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異任内,居然都是意外死亡撵渡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門死嗦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來趋距,“玉大人,你說我怎么就攤上這事越除〗诟” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵摘盆,是天一觀的道長翼雀。 經(jīng)常有香客問我,道長骡澈,這世上最難降的妖魔是什么锅纺? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮肋殴,結(jié)果婚禮上囤锉,老公的妹妹穿的比我還像新娘。我一直安慰自己护锤,他們只是感情好官地,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著烙懦,像睡著了一般驱入。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天亏较,我揣著相機與錄音莺褒,去河邊找鬼。 笑死雪情,一個胖子當(dāng)著我的面吹牛遵岩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播巡通,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼尘执,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了宴凉?” 一聲冷哼從身側(cè)響起誊锭,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎弥锄,沒想到半個月后丧靡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡叉讥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年窘行,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片图仓。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡罐盔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出救崔,到底是詐尸還是另有隱情惶看,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布六孵,位于F島的核電站纬黎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏劫窒。R本人自食惡果不足惜本今,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一甜滨、第九天 我趴在偏房一處隱蔽的房頂上張望荐健。 院中可真熱鬧,春花似錦荠瘪、人聲如沸孕索。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽搞旭。三九已至散怖,卻和暖如春菇绵,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背镇眷。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工咬最, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人偏灿。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓丹诀,卻偏偏與公主長得像,于是被迫代替她去往敵國和親翁垂。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容