色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

使用JavaCV調用USB攝像頭進行實時畫面的展示和拍照

OpenCV學堂 ? 來源:Java與Android技術棧 ? 作者:fengzhizi715 ? 2022-10-09 10:26 ? 次閱讀

Part1一. 業務背景

我們團隊前段時間做了一款小型的智能硬件,它能夠自動拍攝一些商品的圖片,這些圖片將會出現在電商 App 的詳情頁并進行展示。

基于以上的背景,我們需要一個業務后臺用于發送相應的拍照指令,還需要開發一款軟件(上位機)用于接收拍照指令和操作硬件設備。

Part2二. 原先的實現方式以及痛點

早期為了快速實現功能,我們團隊使用 JavaCV 調用 USB 攝像頭(相機)進行實時畫面的展示和拍照。這樣的好處在于,能夠快速實現產品經理提出的功能,并快速上線。當然,也會遇到一些問題。

我列舉幾個遇到的問題:

軟件體積過大

編譯速度慢

軟件運行時占用大量的內存

對于獲取的實時畫面,不利于在軟件側(客戶端側)調用機器學習或者深度學習的庫,因為整個軟件采用 Java/Kotlin 編寫的。

Part3三. 使用 OpenCV 進行重構

基于上述的原因,我嘗試用 OpenCV 替代 JavaCV 看看能否解決這些問題。

13.1JNI 調用的設計

由于我使用 OpenCV C++ 版本來進行開發,因此在開發之前需要先設計好應用層(我們的軟件主要是采用 Java/Kotlin 編寫的)如何跟 Native 層進行交互的一些的方法。比如:USB 攝像頭(相機)的開啟和關閉、拍照、相機相關參數的設置等等。

為此,設計了一個專門用于圖像處理的類 WImagesProcess(W 是項目的代號),它包含了上述的方法。

objectWImagesProcess{

init{
System.load("${FileUtil.loadPath}WImagesProcess.dll")
}

/**
*算法的版本號
*/
externalfungetVersion():String

/**
*獲取OpenCV對應相機的indexid
*@parampidvid相機的pid、vid
*/
externalfungetCameraIndexIdFromPidVid(pidvid:String):Int

/**
*開啟俯拍相機
*@paramindex相機的indexid
*@paramcameraParaMap相機相關的參數
*@paramlistenerjni層給Java層的回調
*/
externalfunstartTopVideoCapture(index:Int,cameraParaMap:Map,listener:VideoCaptureListener)

/**
*開啟側拍相機
*@paramindex相機的indexid
*@paramcameraParaMap相機相關的參數
*@paramlistenerjni層給Java層的回調
*/
externalfunstartRightVideoCapture(index:Int,cameraParaMap:Map,listener:VideoCaptureListener)

/**
*調用對應的相機拍攝照片,使用時需要將IntArray轉換成BufferedImage
*@paramcameraId1:俯拍相機;2:側拍相機
*/
externalfuntakePhoto(cameraId:Int):IntArray

/**
*設置相機的曝光
*@paramcameraId1:俯拍相機;2:側拍相機
*/
externalfunexposure(cameraId:Int,value:Double):Double

/**
*設置相機的亮度
*@paramcameraId1:俯拍相機;2:側拍相機
*/
externalfunbrightness(cameraId:Int,value:Double):Double

/**
*設置相機的焦距
*@paramcameraId1:俯拍相機;2:側拍相機
*/
externalfunfocus(cameraId:Int,value:Double):Double

/**
*關閉相機,釋放相機的資源
*@paramcameraId1:俯拍相機;2:側拍相機
*/
externalfuncloseVideoCapture(cameraId:Int)
}

其中,VideoCaptureListener 是監聽 USB 攝像頭(相機)行為的 Listener。

interfaceVideoCaptureListener{

/**
*Native層調用相機成功
*/
funonSuccess()

/**
*jni將Native層調用相機獲取每一幀的Mat轉換成IntArray,回調給Java層
*@paramarray回調給Java層的IntArray,Java層可以將其轉化成BufferedImage
*/
funonRead(array:IntArray)

/**
*Native層調用相機失敗
*/
funonFailed()
}

VideoCaptureListener#onRead() 方法是在攝像頭(相機)打開后,會實時將每一幀的數據通過回調的形式返回給應用層。

23.2 JNI && Native 層的實現

定義一個 xxx_WImagesProcess.h,它與應用層的 WImagesProcess 類對應。

#include

#ifndef_Include_xxx_WImagesProcess
#define_Include_xxx_WImagesProcess
#ifdef__cplusplus
extern"C"{
#endif

JNIEXPORTjstringJNICALLJava_xxx_WImagesProcess_getVersion
(JNIEnv*env,jobject);

JNIEXPORTvoidJNICALLJava_xxx_WImagesProcess_startTopVideoCapture
(JNIEnv*env,jobject,intindex,jobjectcameraParaMap,jobjectlistener);

JNIEXPORTvoidJNICALLJava_xxx_WImagesProcess_startRightVideoCapture
(JNIEnv*env,jobject,intindex,jobjectcameraParaMap,jobjectlistener);

JNIEXPORTjintArrayJNICALLJava_xxx_WImagesProcess_takePhoto
(JNIEnv*env,jobject,intcameraId);

JNIEXPORTdoubleJNICALLJava_xxx_WImagesProcess_exposure
(JNIEnv*env,jobject,intcameraId,doublevalue);

JNIEXPORTdoubleJNICALLJava_xxx_WImagesProcess_brightness
(JNIEnv*env,jobject,intcameraId,doublevalue);

JNIEXPORTdoubleJNICALLJava_xxx_WImagesProcess_focus
(JNIEnv*env,jobject,intcameraId,doublevalue);

JNIEXPORTvoidJNICALLJava_xxx_WImagesProcess_closeVideoCapture
(JNIEnv*env,jobject,intcameraId);

JNIEXPORTintJNICALLJava_xxx_WImagesProcess_getCameraIndexIdFromPidVid
(JNIEnv*env,jobject,jstringpidvid);

#ifdef__cplusplus
}
#endif
#endif
#pragmaonce

xxx 代表的是 Java 項目中 WImagesProcess 類所在的 package 名稱。畢竟是公司項目,我不便貼出完整的 package 名稱。不熟悉這種寫法的,可以參考 JNI 的規范。

接下來,需要定義一個 xxx_WImagesProcess.cpp 用于實現上述的方法。

3.2.1 USB 攝像頭(相機)的開啟

僅以 startTopVideoCapture() 為例,它的作用是開啟智能硬件的俯拍相機,該硬件有 2 款相機介紹其中一種實現方式,另一種也很類似。

JNIEXPORTvoidJNICALLJava_xxx_WImagesProcess_startTopVideoCapture
(JNIEnv*env,jobject,intindex,jobjectcameraParaMap,jobjectlistener){
jobjecttopListener=env->NewLocalRef(listener);

std::mapmapOut;
JavaHashMapToStlMap(env,cameraParaMap,mapOut);

jclasslistenerClass=env->GetObjectClass(topListener);
jmethodIDsuccessId=env->GetMethodID(listenerClass,"onSuccess","()V");
jmethodIDreadId=env->GetMethodID(listenerClass,"onRead","([I)V");
jmethodIDfailedId=env->GetMethodID(listenerClass,"onFailed","()V");
jobjectlistenerObject=env->NewLocalRef(listenerClass);


try{
topVideoCapture=wImageProcess.getVideoCapture(index,mapOut);
env->CallVoidMethod(listenerObject,successId);

jintArrayjarray;
topVideoCapture>>topFrame;
int*data=newint[topFrame.total()];
intsize=topFrame.rows*topFrame.cols;
jarray=env->NewIntArray(size);

charr,g,b;

while(topFlag){
topVideoCapture>>topFrame;

for(inti=0;iSetIntArrayRegion(jarray,0,size,(jint*)data);
env->CallVoidMethod(listenerObject,readId,jarray);
waitKey(100);
}
topVideoCapture.release();
env->ReleaseIntArrayElements(jarray,env->GetIntArrayElements(jarray,JNI_FALSE),0);
delete[]data;
}
catch(...){
env->CallVoidMethod(listenerObject,failedId);
}

env->DeleteLocalRef(listenerObject);
env->DeleteLocalRef(topListener);
}

這個方法用了很多 JNI 相關的內容,接下來會簡單說明。

首先,JavaHashMapToStlMap() 方法用于將 Java 的 HashMap 轉換成 C++ STL 的 Map。開啟相機時,需要傳遞相機相關的參數。由于相機需要設置參數很多,因此在應用層使用 HashMap,傳遞到 JNI 層需要將他們進行轉化成 C++ 能用的 Map。

voidJavaHashMapToStlMap(JNIEnv*env,jobjecthashMap,std::map&mapOut){
//GettheMap'sentrySet.
jclassmapClass=env->FindClass("java/util/Map");
if(mapClass==NULL){
return;
}
jmethodIDentrySet=
env->GetMethodID(mapClass,"entrySet","()Ljava/util/Set;");
if(entrySet==NULL){
return;
}
jobjectset=env->CallObjectMethod(hashMap,entrySet);
if(set==NULL){
return;
}
//ObtainaniteratorovertheSet
jclasssetClass=env->FindClass("java/util/Set");
if(setClass==NULL){
return;
}
jmethodIDiterator=
env->GetMethodID(setClass,"iterator","()Ljava/util/Iterator;");
if(iterator==NULL){
return;
}
jobjectiter=env->CallObjectMethod(set,iterator);
if(iter==NULL){
return;
}
//GettheIteratormethodIDs
jclassiteratorClass=env->FindClass("java/util/Iterator");
if(iteratorClass==NULL){
return;
}
jmethodIDhasNext=env->GetMethodID(iteratorClass,"hasNext","()Z");
if(hasNext==NULL){
return;
}
jmethodIDnext=
env->GetMethodID(iteratorClass,"next","()Ljava/lang/Object;");
if(next==NULL){
return;
}
//GettheEntryclassmethodIDs
jclassentryClass=env->FindClass("java/util/Map$Entry");
if(entryClass==NULL){
return;
}
jmethodIDgetKey=
env->GetMethodID(entryClass,"getKey","()Ljava/lang/Object;");
if(getKey==NULL){
return;
}
jmethodIDgetValue=
env->GetMethodID(entryClass,"getValue","()Ljava/lang/Object;");
if(getValue==NULL){
return;
}
//IterateovertheentrySet
while(env->CallBooleanMethod(iter,hasNext)){
jobjectentry=env->CallObjectMethod(iter,next);
jstringkey=(jstring)env->CallObjectMethod(entry,getKey);
jstringvalue=(jstring)env->CallObjectMethod(entry,getValue);
constchar*keyStr=env->GetStringUTFChars(key,NULL);
if(!keyStr){
return;
}
constchar*valueStr=env->GetStringUTFChars(value,NULL);
if(!valueStr){
env->ReleaseStringUTFChars(key,keyStr);
return;
}

mapOut.insert(std::make_pair(string(keyStr),string(valueStr)));

env->DeleteLocalRef(entry);
env->ReleaseStringUTFChars(key,keyStr);
env->DeleteLocalRef(key);
env->ReleaseStringUTFChars(value,valueStr);
env->DeleteLocalRef(value);
}
}

接下來幾行,表示將應用層傳遞的 VideoCaptureListener 在 JNI 層需要獲取其類型。然后,查找 VideoCaptureListener 中的幾個方法,便于后面調用。這樣 JNI 層就可以跟應用層的 Java/Kotlin 進行交互了。

jclasslistenerClass=env->GetObjectClass(topListener);
jmethodIDsuccessId=env->GetMethodID(listenerClass,"onSuccess","()V");
jmethodIDreadId=env->GetMethodID(listenerClass,"onRead","([I)V");
jmethodIDfailedId=env->GetMethodID(listenerClass,"onFailed","()V");

接下來,開始打開攝像頭(相機),并回調給應用層,這樣 VideoCaptureListener#onSuccess() 方法就能收到回調。

topVideoCapture=wImageProcess.getVideoCapture(index,mapOut);
env->CallVoidMethod(listenerObject,successId);

打開攝像頭(相機)后,就可以實時把獲取的每一幀返回給應用層。同樣,VideoCaptureListener#onRead() 方法就能收到回調。

while(topFlag){
topVideoCapture>>topFrame;

for(inti=0;iSetIntArrayRegion(jarray,0,size,(jint*)data);
env->CallVoidMethod(listenerObject,readId,jarray);
waitKey(100);
}

后面的代碼是關閉相機,釋放資源。

3.2.2 打開相機,設置相機參數

在 3.2.1 中,有以下這樣一段代碼:

topVideoCapture=wImageProcess.getVideoCapture(index,mapOut);

它的用途是通過 index id 打開對應的相機,并設置相機需要的參數,最后返回 VideoCapture 對象。

VideoCaptureWImageProcess::getVideoCapture(intindex,std::mapcameraParaMap){
VideoCapturecapture(index);

for(auto&t:cameraParaMap){
intkey=stoi(t.first);
doublevalue=stod(t.second);
capture.set(key,value);
}

returncapture;
}

對于存在同時調用多個相機的情況,OpenCV 需要基于 index id 來獲取對應的相機。那如何獲取 index id 呢?以后有機會再寫一篇文章吧。

WImagesProcess 類還額外提供了多個方法用于設置相機的曝光、亮度、焦距等。我們在啟動相機的時候不是可以通過 HashMap 來傳遞相機需要的參數嘛,為何還提供這些方法呢?這樣做的目的是因為針對不同商品拍照時,可能會調節相機相關的參數,因此 WImagesProcess 類提供了這些方法。

3.2.3 拍照

基于 cameraId 來找到對應的相機進行拍照,并將結果返回給應用層,唯一需要注意的是 C++ 得手動釋放資源。

JNIEXPORTjintArrayJNICALLJava_xxx_WImagesProcess_takePhoto
(JNIEnv*env,jobject,intcameraId){

Matmat;
if(cameraId==1){
mat=topFrame;
}
elseif(cameraId==2){
mat=rightFrame;
}

int*data=newint[mat.total()];

charr,g,b;

for(inti=0;iNewIntArray(size);
env->SetIntArrayRegion(jarray,0,size,_data);
delete[]data;
returnjarray;
}

最后,將 CV 程序和 JNI 相關的代碼最終編譯成一個 dll 文件,供軟件(上位機)調用,實現最終的需求。

33.3 應用層的調用

上述代碼寫好后,攝像頭(相機)在應用層的打開就非常簡單了,大致的代碼如下:

valmap=HashMap()
map[CAP_PROP_FRAME_WIDTH]=4208.toString()
map[CAP_PROP_FRAME_HEIGHT]=3120.toString()
map[CAP_PROP_AUTO_EXPOSURE]=0.25.toString()
map[CAP_PROP_EXPOSURE]=getTopExposure()
map[CAP_PROP_GAIN]=getTopFocus()
map[CAP_PROP_BRIGHTNESS]=getTopBrightness()
WImagesProcess.startTopVideoCapture(index+CAP_DSHOW,map,object:VideoCaptureListener{
overridefunonSuccess(){
......
}

overridefunonRead(array:IntArray){
......
}

overridefunonFailed(){
......
}
})

應用層的拍照也很簡單:

valbufferedImage=WImagesProcess.takePhoto(cameraId).toBufferedImage()

其中,toBufferedImage() 是 Kotlin 的擴展函數。因為 takePhoto() 方法返回 IntArray 對象。

funIntArray.toBufferedImage():BufferedImage{
valdestImage=BufferedImage(FRAME_WIDTH,FRAME_HEIGHT,BufferedImage.TYPE_INT_RGB)
destImage.setRGB(0,0,FRAME_WIDTH,FRAME_HEIGHT,this,0,FRAME_WIDTH)
returndestImage
}

這樣,對于應用層的調用是非常簡單的。

Part4四. 總結

通過 OpenCV 替換 JavaCV 之后,軟件遇到的痛點問題基本可以解決。例如軟件體積明顯變小了。

2416f652-4197-11ed-96c9-dac502259ad0.png

另外,軟件在運行時占用大量內存的情況也得到明顯改善。如果需要在展示實時畫面時,對圖像做一些處理,也可以在 Native 層使用 OpenCV 來處理每一幀,然后將結果返回給應用層。





審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 圖像處理
    +關注

    關注

    27

    文章

    1299

    瀏覽量

    56837
  • OpenCV
    +關注

    關注

    31

    文章

    635

    瀏覽量

    41453
  • USB攝像頭
    +關注

    關注

    0

    文章

    22

    瀏覽量

    11317

原文標題:OpenCV + Kotlin 實現 USB 攝像頭(相機)實時畫面、拍照

文章出處:【微信號:CVSCHOOL,微信公眾號:OpenCV學堂】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    【飛凌嵌入式OK3576-C開發板體驗】 USB攝像頭拍照測試

    USB攝像頭拍照USB 攝像頭插入開發板,將自動安裝 uvc 驅動 使用命令查看usb
    發表于 10-10 09:24

    解決USB攝像頭所導致的花屏故障

    成像畫面立即恢復正常了??磥?b class='flag-5'>攝像頭花屏故障是由于計算機原先的USB端口損壞引起的,于是筆者又對原先的USB端口進行了檢查,最后發現原先的
    發表于 03-16 09:53

    求一個用labview編寫的控制攝像頭拍照及保存的例子,謝謝!

    求一個用labview編寫的控制攝像頭拍照及保存的例子,謝謝!調用攝像頭應該是怎么調用,然后調用
    發表于 03-15 13:05

    攝像頭用色溫鏡擋住拍照很暗

    `大家好,最近在弄一個串口攝像頭,攝像頭sensor是OV7725,DSP芯片是CL8529?,F在有個問題,用藍色的色溫鏡擋住攝像頭的時候,拍照很暗,同時晚上紅外
    發表于 06-25 11:27

    android多攝像頭同時預覽

    程序可以打開,預覽畫面還算流暢,大概25幀左右。調試USB攝像頭通過UVC生成了video節點,接著移植了camera的HAL層庫文件,可以順利打開。當要調用第二個
    發表于 01-18 14:55

    自制USB攝像頭程序,可拍照,可以錄制視頻

    `利用NI Vision做的USB攝像頭程序如果有多個USB攝像頭,可以從其中選擇想要用的攝像頭可以拍照
    發表于 03-14 12:17

    怎樣通過串口傳輸攝像頭畫面

    各位大神們,幫幫忙啊~~~~~怎樣通過串口顯示攝像頭畫面呢讓畫面實時顯示在labview前面板上面。。當然 最好有個做好的小例子參考一下就最好啦
    發表于 03-08 10:25

    labview調用USB攝像頭無法選擇其它攝像頭

    labview調用USB攝像頭無法選擇其它攝像頭,選cam1但是程序采集的圖像是cam0的圖像 補充內容 (2017-9-12 15:48): 把session in改變為常量也不管
    發表于 09-12 10:05

    請問USB攝像頭怎么在樹莓派中實現拍照功能?

    求助各位大神USB攝像頭怎么在樹莓派中實現拍照功能
    發表于 05-28 05:56

    NI vision調用筆記本攝像頭拍照做的子程序

    用NI Vision控件做的一個調用筆記本攝像頭拍照的程序
    發表于 09-30 14:37

    OV7670攝像頭模塊是如何實現攝像頭畫面上傳到onenet的

    OV7670攝像頭模塊是如何實現攝像頭畫面上傳到onenet的?
    發表于 09-30 08:53

    【賽昉科技昉·星光RISC-V單板計算機試用體驗】使用??低?b class='flag-5'>USB攝像頭拍照和錄制視頻

    拍照部分,我使用了camorama,可以實時預覽攝像頭畫面,并且在界面上點擊拍照。錄制視頻部分,可以試用的軟件很多,我選擇了使用命令行的f
    發表于 07-17 11:42

    基于LABVIEW編程的USB攝像頭拍照VI文件

    LABVIEW2018編寫的USB攝像頭攝像拍照子VI,給有需要的朋友。
    發表于 09-15 14:31 ?56次下載

    LDR6023Q給USB攝像頭帶來的神奇作用

    USB攝像頭是一款支持USB攝像頭USB視頻采集卡等USB設備通過OTG連接手機并驅動設備
    的頭像 發表于 07-28 10:54 ?1034次閱讀
    LDR6023Q給<b class='flag-5'>USB</b><b class='flag-5'>攝像頭</b>帶來的神奇作用

    LDR6023Q是如何運用在USB攝像頭轉接器的?

    USB攝像頭是一款支持USB攝像頭USB視頻采集卡等USB設備通過OTG連接手機并驅動設備
    的頭像 發表于 10-21 09:52 ?704次閱讀
    LDR6023Q是如何運用在<b class='flag-5'>USB</b><b class='flag-5'>攝像頭</b>轉接器的?
    主站蜘蛛池模板: 亚洲伊人久久大香线蕉综合图片| 亚洲成AV人电影在线观看| 婷婷综合亚洲爱久久| 99无人区码一码二码三| 九九夜夜妹子| 亚洲综合中文| 久操久操久操| 影音先锋色av男人资源网| 精品午夜视频| 一本道mw高清码二区三区| 久草热在线| 最新无码专区在线视频| 美女张开腿露尿口给男人亲| 757一本到午夜宫| 男人把女人桶到爽免费看视频| 91精品一区二区三区在线观看| 伦理片97影视网| 99re28久久热在线观看| 秋霞伦理机在线看片| 擦擦擦在线视频观看| 四虎视频最新视频在线观看| 国产午夜精品久久久久九九| 亚洲精品乱码一区二区三区 | 欧美 亚洲综合在线一区| 99久久伊人一区二区yy5099| 日本在线免费| 国精产品一区二区三区有限公司| 妖精视频一区二区免费| 美女扒开尿孔| 高清国产mv视频在线观看| 羞羞漫画免费漫画页面在线看漫画秋蝉 | 久久久久青草大香线综合精品| 2021国产精品一卡2卡三卡4卡| 起碰免费公开97在线视频| 国产精品欧美亚洲| 一二三四视频免费社区5| 嗯别插太快好深再深点| 国产精品婷婷五月久久久久| 一品道门免费视频韩国| 男女疯狂一边摸一边做羞羞视频| 福利视频久久|