0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

鴻蒙上成功調(diào)用相機(jī)!

OpenHarmony技術(shù)社區(qū) ? 來(lái)源:OST開源開發(fā)者 ? 2023-02-14 09:34 ? 次閱讀

相機(jī)是一個(gè)系統(tǒng)的基礎(chǔ)能力,能夠通過調(diào)用相機(jī)進(jìn)行拍照,在很多場(chǎng)景下都會(huì)使用到相機(jī)的調(diào)用,如人臉識(shí)別門禁,人臉解鎖等操作。

本文主要介紹在 OpenHarmony 應(yīng)用開發(fā)中 ArkUI 開發(fā)框架下相機(jī)應(yīng)用的開發(fā)。

開發(fā)模式:Stage 開發(fā)模式

SDK 版本:3.2.2.5

開發(fā)環(huán)境:DevEco Studio 3.0 Release 3.0.0.993

實(shí)現(xiàn)步驟

①聲明權(quán)限

在 module.json5 中配置權(quán)限:

"reqPermissions":[{
"name":"ohos.permission.LOCATION",
},
{
"name":"ohos.permission.CAMERA"
},
{
"name":"ohos.permission.MICROPHONE"
},
{
"name":"ohos.permission.MEDIA_LOCATION"
},
{
"name":"ohos.permission.WRITE_MEDIA"
},
{
"name":"ohos.permission.READ_MEDIA"
}]

在 MainAbility.ts 中調(diào)用 requestPermissionsFromUser 方法申請(qǐng)權(quán)限:

constPERMISSIONS:Array=[
'ohos.permission.CAMERA',
'ohos.permission.MICROPHONE',
'ohos.permission.MEDIA_LOCATION',
'ohos.permission.READ_MEDIA',
'ohos.permission.WRITE_MEDIA',
'ohos.permission.GET_WIFI_INFO',
'ohos.permission.GET_WIFI_PEERS_MAC',
]

globalThis.abilityWant=want;
globalThis.context=this.context
globalThis.abilityContext=this.context;

globalThis.context.requestPermissionsFromUser(PERMISSIONS).then((message)=>{
console.log(JSON.stringify(message))
})

注意:權(quán)限需要在頁(yè)面加載前提前申請(qǐng),所以需要在調(diào)用相機(jī)的頁(yè)面前添加一個(gè)過渡的頁(yè)面。

②準(zhǔn)備工作

導(dǎo)包:

importcamerafrom'@ohos.multimedia.camera';
importimagefrom'@ohos.multimedia.image';
importfileiofrom'@ohos.fileio';
importmediaLibraryfrom'@ohos.multimedia.mediaLibrary'
constCameraSize={
WIDTH:640,
HEIGHT:480
}
定義變量:
privatemXComponentController=newXComponentController()
privatecameraManager:camera.CameraManager=undefined
privatecameras:Array=undefined
privatecameraId:string=undefined
privatemReceiver:image.ImageReceiver=undefined
privatecameraInput:camera.CameraInput=undefined
privatepreviewOutput:camera.PreviewOutput=undefined
privatemSurfaceId:string=undefined
privatephotoOutput:camera.PhotoOutput=undefined
privatecaptureSession:camera.CaptureSession=undefined
privatemediaUtil:MediaUtil=undefined
@StatedesStr:string=""
privatefileAsset:mediaLibrary.FileAsset
privatesurfaceId:number
@StatephotoUriMedia:string=""
privatephotoFlag:boolean=true
@StateimgUrl:string=""
@StateisMediaUrl:boolean=true//判斷保存路徑為是沙箱路徑或者媒體路徑,默認(rèn)媒體路徑
aboutToAppear(){
this.mediaTest=mediaLibrary.getMediaLibrary(globalThis.context)
}
工具方法:
asynccreateAndGetUri(mediaType:number){
letinfo=this.getInfoFromType(mediaType)
letdateTimeUtil=newDateTimeUtil()
letname=`${dateTimeUtil.getDate()}_${dateTimeUtil.getTime()}`
letdisplayName=`${info.prefix}${name}${info.suffix}`
letpublicPath=awaitthis.mediaTest.getPublicDirectory(info.directory)
letdataUri=awaitthis.mediaTest.createAsset(mediaType,displayName,publicPath)
returndataUri
}
asyncgetFdPath(fileAsset:any){
letfd=awaitfileAsset.open('Rw')
returnfd
}
getInfoFromType(mediaType:number){
letresult={
prefix:'',suffix:'',directory:0
}
switch(mediaType){
casemediaLibrary.MediaType.FILE:
result.prefix='FILE_'
result.suffix='.txt'
result.directory=mediaLibrary.DirectoryType.DIR_DOCUMENTS
break
casemediaLibrary.MediaType.IMAGE:
result.prefix='IMG_'
result.suffix='.jpg'
result.directory=mediaLibrary.DirectoryType.DIR_IMAGE
break
casemediaLibrary.MediaType.VIDEO:
result.prefix='VID_'
result.suffix='.mp4'
result.directory=mediaLibrary.DirectoryType.DIR_VIDEO
break
casemediaLibrary.MediaType.AUDIO:
result.prefix='AUD_'
result.suffix='.wav'
result.directory=mediaLibrary.DirectoryType.DIR_AUDIO
break
}
returnresult
}
工具類:
/**
*@file日期工具
*/
exportdefaultclassDateTimeUtil{

/**
*時(shí)分秒
*/
getTime(){
constDATETIME=newDate()
returnthis.concatTime(DATETIME.getHours(),DATETIME.getMinutes(),DATETIME.getSeconds())
}

/**
*年月日
*/
getDate(){
constDATETIME=newDate()
returnthis.concatDate(DATETIME.getFullYear(),DATETIME.getMonth()+1,DATETIME.getDate())
}

/**
*日期不足兩位補(bǔ)充0
*@paramvalue-數(shù)據(jù)值
*/
fill(value:number){
return(value>9?'':'0')+value
}

/**
*年月日格式修飾
*@paramyear
*@parammonth
*@paramdate
*/
concatDate(year:number,month:number,date:number){
return`${year}${this.fill(month)}${this.fill(date)}`
}

/**
*時(shí)分秒格式修飾
*@paramhours
*@paramminutes
*@paramseconds
*/
concatTime(hours:number,minutes:number,seconds:number){
return`${this.fill(hours)}${this.fill(minutes)}${this.fill(seconds)}`
}
}

這個(gè)工具類主要是用來(lái)進(jìn)行獲取時(shí)間對(duì)相片進(jìn)行命名的工具類。

③構(gòu)建 UI 組件

頁(yè)面主要分為 2 塊,左邊為相機(jī)的 XComponent 組件,右邊為圖片顯示區(qū)域。拍完的照片能夠顯示在右邊。 XComponent 組件作用于 EGL/OpenGLES 和媒體數(shù)據(jù)寫入,并顯示在 XComponent 組件。

相關(guān)資料

https://developer.harmonyos.com/cn/docs/documentation/doc-references/ts-basic-components-xcomponent-0000001333800561

hml 代碼如下:

build(){
Flex(){
Flex(){
Stack(){
Flex(){
//相機(jī)顯示的組件
XComponent({
id:'componentId',
type:'surface',
controller:this.mXComponentController
}).onLoad(()=>{
this.mXComponentController.setXComponentSurfaceSize({surfaceWidth:640,surfaceHeight:480})
this.surfaceId=this.mXComponentController.getXComponentSurfaceId()
this.initCamera(this.surfaceId)
})
}.width(800).height(800)
//顯示在相機(jī)上面的組件:拍照和攝像的圖標(biāo),攝像的時(shí)間
Flex({direction:FlexDirection.Column,justifyContent:FlexAlign.End,alignItems:ItemAlign.Center}){
if(this.photoFlag){//拍照
Image($r("app.media.take_photo_normal")).width(50).height(50).onClick(()=>{
this.desStr="拍照完成"
this.takePicture()
})
}
Text(this.desStr).fontColor("red").height(30).fontSize(20)
}.width(480).height(480)
}.border({width:1,style:BorderStyle.Solid,color:"#000000"})
//右邊的控制button和圖片顯示區(qū)域
Flex({
direction:FlexDirection.Column,
justifyContent:FlexAlign.SpaceBetween,
alignItems:ItemAlign.Center,
}){
Button("選擇沙箱路徑存儲(chǔ)").onClick(()=>{
this.isMediaUrl=false
}).stateStyles({
normal:{//設(shè)置默認(rèn)情況下的顯示樣式
.backgroundColor(Color.Blue)
},
pressed:{//設(shè)置手指摁下時(shí)的顯示樣式
.backgroundColor(Color.Pink)
}
})
Image(decodeURI("file://"+this.imgUrl)).width(480).height(350)//顯示沙箱圖片
Button("選擇媒體路徑存儲(chǔ)").onClick(()=>{
this.isMediaUrl=true
}).stateStyles({
normal:{//設(shè)置默認(rèn)情況下的顯示樣式
.backgroundColor(Color.Blue)
},
pressed:{//設(shè)置手指摁下時(shí)的顯示樣式
.backgroundColor(Color.Pink)
}
})
Image(decodeURI(this.imgUrl)).width(480).height(350)//顯示媒體圖片
}.width(480).height("100%").border({width:1,style:BorderStyle.Solid,color:"#000000"})

}.border({width:1,style:BorderStyle.Solid,color:"red"})
.width("100%").height("100%")
}
.height('100%').width("100%")
}
UI 實(shí)現(xiàn)了對(duì)存儲(chǔ)路徑的選擇,需要存儲(chǔ)到沙箱路徑還是媒體路徑。

注意:沙箱路徑需要加上"file://",查看對(duì)應(yīng)的存儲(chǔ)路徑步驟:

打開 hdc 命令窗口

cd /data/app/el2/100/base/com.chinasoft.photo/haps/entry/files進(jìn)入

ls 查看全部文件

④拍照流程

初始化相機(jī):這一步需要在拍照前就進(jìn)行,一般是在 XComponent 組件的 onLoad() 中進(jìn)行的。

//初始化相機(jī)和會(huì)話管理
asyncinitCamera(surfaceId:number){
this.cameraManager=awaitcamera.getCameraManager(globalThis.context)//需要在Ability中定義globalThis.context=this.context
this.cameras=awaitthis.cameraManager.getCameras()
this.cameraId=this.cameras[1].cameraId
awaitthis.photoReceiver()//創(chuàng)建圖片接收器并進(jìn)行訂閱
this.mSurfaceId=awaitthis.mReceiver.getReceivingSurfaceId()
this.cameraInput=awaitthis.cameraManager.createCameraInput(this.cameraId)
this.previewOutput=awaitcamera.createPreviewOutput(surfaceId.toString())
this.photoOutput=awaitcamera.createPhotoOutput(this.mSurfaceId)

this.captureSession=awaitcamera.createCaptureSession(globalThis.context)
awaitthis.captureSession.beginConfig()
awaitthis.captureSession.addInput(this.cameraInput)
awaitthis.captureSession.addOutput(this.previewOutput)
awaitthis.captureSession.addOutput(this.photoOutput)
awaitthis.captureSession.commitConfig()
awaitthis.captureSession.start().then(()=>{
console.log('zmw1--Promisereturnedtoindicatethesessionstartsuccess.');
})
}
//創(chuàng)建圖片接收器并進(jìn)行訂閱
asyncphotoReceiver(){
this.mReceiver=image.createImageReceiver(CameraSize.WIDTH,CameraSize.HEIGHT,4,8)
letbuffer=newArrayBuffer(4096)
this.mReceiver.on('imageArrival',()=>{
console.log("zmw-service-imageArrival")
this.mReceiver.readNextImage((err,image)=>{
if(err||image===undefined){
return
}
image.getComponent(4,(errMsg,img)=>{
if(errMsg||img===undefined){
return
}
if(img.byteBuffer){
buffer=img.byteBuffer
}
if(this.isMediaUrl){
this.savePictureMedia(buffer,image)
}else{
this.savePictureSand(buffer,image)
}
})
})
returnbuffer
})
}
如下:

根據(jù) camera 的 getCameraManager 方法獲取 CameraManager

通過 CameraManager 獲取所有的相機(jī)數(shù)組,找到可用的相機(jī),并獲取相機(jī)的 cameraid

創(chuàng)建圖片接收器并進(jìn)行訂閱,獲取 receiver 的 surfaceId

通過 CameraManager 的 createCameraInput(cameraid) 創(chuàng)建相機(jī)輸入流

通過 camera 的 createPreviewOutput(sufaceId) 創(chuàng)建相機(jī)預(yù)覽輸出流,這里 sufaceId 為 XComponent 的 id

通過 camera 的 createPhotoOutput(sufaceId) 創(chuàng)建相機(jī)拍照輸出流,這里 sufaceId 為圖片接收器的 surfaceId

會(huì)話管理:創(chuàng)建會(huì)話,并且配置會(huì)話的相機(jī)輸入流,相機(jī)拍照輸出流與相機(jī)預(yù)覽流,提交配置,開始會(huì)話

至此,相機(jī)就能正常的顯示出圖像了。

用拍照方法拍攝照片:

//拍攝照片
asynctakePicture(){
letphotoSettings={
rotation:camera.ImageRotation.ROTATION_0,
quality:camera.QualityLevel.QUALITY_LEVEL_LOW,
mirror:false
}
awaitthis.photoOutput.capture(photoSettings)
}

調(diào)用相機(jī)的輸出流的 capture 方法進(jìn)行拍照操作,會(huì)觸發(fā)圖片接收器的監(jiān)聽,進(jìn)行對(duì)字節(jié)流的寫入操作,保存到沙箱或者媒體。

保存圖片:分為沙箱路徑與媒體路徑。

//保存沙箱路徑
asyncsavePictureSand(buffer:ArrayBuffer,img:image.Image){
letinfo=this.mediaUtil.getInfoFromType(mediaLibrary.MediaType.IMAGE)
letdateTimeUtil=newDateTimeUtil()
letname=`${dateTimeUtil.getDate()}_${dateTimeUtil.getTime()}`
letdisplayName=`${info.prefix}${name}${info.suffix}`
letsandboxDirPath=globalThis.context.filesDir;
letpath=sandboxDirPath+'/'+displayName
this.imgUrl=path
letfdSand=awaitfileio.open(path,0o2|0o100,0o666);
awaitfileio.write(fdSand,buffer)
awaitfileio.close(fdSand).then(()=>{
this.desStr=""
});
awaitimg.release()
}
//保存媒體路徑
asyncsavePictureMedia(buffer:ArrayBuffer,img:image.Image){
this.fileAsset=awaitthis.mediaUtil.createAndGetUri(mediaLibrary.MediaType.IMAGE)
this.imgUrl=this.fileAsset.uri
letfd=awaitthis.mediaUtil.getFdPath(this.fileAsset)
awaitfileio.write(fd,buffer)
awaitthis.fileAsset.close(fd).then(()=>{
this.desStr=""
})
awaitimg.release()
}
釋放相機(jī):
//結(jié)束釋放相機(jī)資源
asyncreleaseCamera(){
if(this.captureSession){
awaitthis.captureSession.stop().then(()=>{
})
}
if(this.cameraInput){
awaitthis.cameraInput.release().then(()=>{
})
}
if(this.previewOutput){
awaitthis.previewOutput.release().then(()=>{
})
}
if(this.photoOutput){
awaitthis.photoOutput.release().then(()=>{
})
}
//釋放會(huì)話
if(this.captureSession){
awaitthis.captureSession.release((err)=>{
if(err){
console.error('zmwFailedtoreleasetheCaptureSessioninstance${err.message}');
return;
}
});
}
}

在完成了相機(jī)的調(diào)用后,需要對(duì)相機(jī)的資源進(jìn)行釋放,否則再次調(diào)用的時(shí)候會(huì)一直被占用而導(dǎo)致黑屏。

總結(jié)

OpenHarmony 對(duì)于相機(jī)的官方使用文檔不太清晰,有許多的坑,需要去趟。

在這個(gè)過程中我遇到的問題:

在相機(jī)的使用時(shí),由于開發(fā)板上的相機(jī)獲取到了兩個(gè),一個(gè)是外接 USB 的相機(jī),一個(gè)應(yīng)該是系統(tǒng)的,在獲取相機(jī)的 id 的時(shí)候需要注意。

在保存相機(jī)拍照的圖片的時(shí)候,保存到沙箱路徑時(shí)顯示不到頁(yè)面上,需要在保存的路徑前加上"file://"。

需要擴(kuò)展研究的是進(jìn)行相機(jī)的攝像操作,以及相機(jī)拍照與攝像的切換操作。

審核編輯:湯梓紅
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 相機(jī)
    +關(guān)注

    關(guān)注

    4

    文章

    1337

    瀏覽量

    53442
  • Module
    +關(guān)注

    關(guān)注

    0

    文章

    66

    瀏覽量

    12837
  • SDK
    SDK
    +關(guān)注

    關(guān)注

    3

    文章

    1020

    瀏覽量

    45699
  • 鴻蒙
    +關(guān)注

    關(guān)注

    57

    文章

    2303

    瀏覽量

    42693
  • OpenHarmony
    +關(guān)注

    關(guān)注

    25

    文章

    3641

    瀏覽量

    16066

原文標(biāo)題:鴻蒙上成功調(diào)用相機(jī)!

文章出處:【微信號(hào):gh_834c4b3d87fe,微信公眾號(hào):OpenHarmony技術(shù)社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    STM32上成功移植了emWim支持觸摸屏,但是效果不好?

    我在STM32上成功移植了emWim,并成功支持了觸摸屏,但是效果不好觸摸屏的精度不高,大概有5mm左右的偏差而且感覺觸摸屏的反應(yīng)很慢點(diǎn)著屏幕一會(huì)兒,光標(biāo)才移動(dòng),怎么解決這兩個(gè)問題????
    發(fā)表于 04-30 06:36

    如何在FPGA板上成功運(yùn)行VHDL程序?

    我無(wú)法在12/21/2017找到我的帖子,所以我決定使用這個(gè)帖子重新發(fā)帖。問題:盡管如下,我無(wú)法在FPGA板上成功運(yùn)行VHDL程序:1.生成編程文件 - 成功完成2.位文件 - 成功完成3.
    發(fā)表于 08-08 10:00

    鴻蒙系統(tǒng)更新后新增相機(jī)多機(jī)位模式

    近日,華為更新了鴻蒙系統(tǒng)HarmonyOS2.0.0.166,這個(gè)系統(tǒng)版本帶來(lái)了一項(xiàng)新功能,就是相機(jī)的多機(jī)位模式,也叫作分布式相機(jī)。這個(gè)功能的特點(diǎn)是只要有多臺(tái)華為手機(jī)或平板,在直播、視頻聊天或拍攝
    發(fā)表于 08-18 15:04

    在STM32F103ZE的一款開發(fā)板上成功移植uCOSIII

    在STM32F103ZE的一款開發(fā)板上成功移植uCOSIII,并建立簡(jiǎn)單的LED測(cè)試工程使用Keil uVision4軟件,3.5固件庫(kù)及v3.02.00版本的uCOSIII
    發(fā)表于 08-23 08:28

    有在i.MX6上成功移植好阿里的mqtt驅(qū)動(dòng)的嗎

    有在i.MX6上成功移植好阿里的mqtt驅(qū)動(dòng)的么?
    發(fā)表于 01-07 07:09

    如何讓U-Boot在RK3399這塊板子上成功的運(yùn)行起來(lái)呢

    U-Boot加載方式是怎樣的?如何讓U-Boot在RK3399這塊板子上成功的運(yùn)行起來(lái)呢?
    發(fā)表于 03-07 06:03

    請(qǐng)問鴻蒙hap包是否支持插件化開發(fā)?

    如題,安卓上可以使用dexclassloader機(jī)制動(dòng)態(tài)加載其他apk作為插件使用,鴻蒙上用類似的能力嗎?有什么解決方案呢?
    發(fā)表于 06-16 11:34

    鴻蒙上能安裝docker嗎?

    android是不支持安裝docker的,想了解下鴻蒙是否支持安裝docker
    發(fā)表于 03-21 16:32

    鴻蒙上使用Python進(jìn)行物聯(lián)網(wǎng)編程

    在上一篇帖子《使用 Python 開發(fā)鴻蒙設(shè)備程序(1-GPIO 外設(shè)控制)》中,已經(jīng)成功的使用 Python 對(duì) GPIO 上的外設(shè)進(jìn)行了控制。 這其實(shí)不是什么大不了的事,從功能的角度也著實(shí)不值得
    的頭像 發(fā)表于 09-28 09:55 ?4230次閱讀
    在<b class='flag-5'>鴻蒙上</b>使用Python進(jìn)行物聯(lián)網(wǎng)編程

    鴻蒙上安裝按鈕實(shí)現(xiàn)下載、暫停、取消、顯示等操作

    今天給大家分享在鴻蒙上一個(gè)按鈕實(shí)現(xiàn)下載、暫停、取消、顯示下載進(jìn)度操作。
    的頭像 發(fā)表于 01-04 14:32 ?2226次閱讀

    鴻蒙上實(shí)現(xiàn)“數(shù)字華容道”小游戲

    本篇文章教大家如何在鴻蒙上實(shí)現(xiàn)“數(shù)字華容道”小游戲。
    的頭像 發(fā)表于 12-26 09:52 ?1174次閱讀

    鴻蒙上實(shí)現(xiàn)簡(jiǎn)單的“每日新聞”

    這是一篇講解如何實(shí)現(xiàn)基于鴻蒙 JS 的簡(jiǎn)單的每日新聞。
    的頭像 發(fā)表于 12-26 09:58 ?820次閱讀

    鴻蒙上點(diǎn)亮LED燈

    上一篇我們成功的在鴻蒙開發(fā)板上輸出了 Hello World!這一篇將帶大家點(diǎn)亮 LED 燈。
    的頭像 發(fā)表于 01-16 10:28 ?2003次閱讀

    鴻蒙上開發(fā)“小蜜蜂”游戲

    小時(shí)候我們有個(gè)熟悉的游戲叫小蜜蜂。本文教大家在鴻蒙上學(xué)做這個(gè)小蜜蜂游戲。
    的頭像 發(fā)表于 04-03 11:27 ?1601次閱讀

    鴻蒙實(shí)戰(zhàn)開發(fā):【相機(jī)和媒體庫(kù)】

    在ArkTS中調(diào)用相機(jī)拍照和錄像,以及如何使用媒體庫(kù)接口進(jìn)行媒體文件的增、刪、改、查操作。本示例用到了
    的頭像 發(fā)表于 03-20 16:36 ?711次閱讀
    <b class='flag-5'>鴻蒙</b>實(shí)戰(zhàn)開發(fā):【<b class='flag-5'>相機(jī)</b>和媒體庫(kù)】