相機是一個系統(tǒng)的基礎能力,能夠通過調(diào)用相機進行拍照,在很多場景下都會使用到相機的調(diào)用,如人臉識別門禁,人臉解鎖等操作。
本文主要介紹在 OpenHarmony 應用開發(fā)中 ArkUI 開發(fā)框架下相機應用的開發(fā)。
開發(fā)模式:Stage 開發(fā)模式
SDK 版本:3.2.2.5
開發(fā)環(huán)境:DevEco Studio 3.0 Release 3.0.0.993
實現(xiàn)步驟
①聲明權限
在 module.json5 中配置權限:
"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 方法申請權限:
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)) })
注意:權限需要在頁面加載前提前申請,所以需要在調(dià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//判斷保存路徑為是沙箱路徑或者媒體路徑,默認媒體路徑 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{
/**
*時分秒
*/
getTime(){
constDATETIME=newDate()
returnthis.concatTime(DATETIME.getHours(),DATETIME.getMinutes(),DATETIME.getSeconds())
}
/**
*年月日
*/
getDate(){
constDATETIME=newDate()
returnthis.concatDate(DATETIME.getFullYear(),DATETIME.getMonth()+1,DATETIME.getDate())
}
/**
*日期不足兩位補充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)}`
}
/**
*時分秒格式修飾
*@paramhours
*@paramminutes
*@paramseconds
*/
concatTime(hours:number,minutes:number,seconds:number){
return`${this.fill(hours)}${this.fill(minutes)}${this.fill(seconds)}`
}
}
這個工具類主要是用來進行獲取時間對相片進行命名的工具類。
③構建 UI 組件
頁面主要分為 2 塊,左邊為相機的 XComponent 組件,右邊為圖片顯示區(qū)域。拍完的照片能夠顯示在右邊。 XComponent 組件作用于 EGL/OpenGLES 和媒體數(shù)據(jù)寫入,并顯示在 XComponent 組件。
相關資料:
https://developer.harmonyos.com/cn/docs/documentation/doc-references/ts-basic-components-xcomponent-0000001333800561
hml 代碼如下:
build(){
Flex(){
Flex(){
Stack(){
Flex(){
//相機顯示的組件
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)
//顯示在相機上面的組件:拍照和攝像的圖標,攝像的時間
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("選擇沙箱路徑存儲").onClick(()=>{
this.isMediaUrl=false
}).stateStyles({
normal:{//設置默認情況下的顯示樣式
.backgroundColor(Color.Blue)
},
pressed:{//設置手指摁下時的顯示樣式
.backgroundColor(Color.Pink)
}
})
Image(decodeURI("file://"+this.imgUrl)).width(480).height(350)//顯示沙箱圖片
Button("選擇媒體路徑存儲").onClick(()=>{
this.isMediaUrl=true
}).stateStyles({
normal:{//設置默認情況下的顯示樣式
.backgroundColor(Color.Blue)
},
pressed:{//設置手指摁下時的顯示樣式
.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 實現(xiàn)了對存儲路徑的選擇,需要存儲到沙箱路徑還是媒體路徑。
注意:沙箱路徑需要加上"file://",查看對應的存儲路徑步驟:
打開 hdc 命令窗口
cd /data/app/el2/100/base/com.chinasoft.photo/haps/entry/files進入
ls 查看全部文件
④拍照流程
初始化相機:這一步需要在拍照前就進行,一般是在 XComponent 組件的 onLoad() 中進行的。
//初始化相機和會話管理
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)建圖片接收器并進行訂閱
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)建圖片接收器并進行訂閱
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 獲取所有的相機數(shù)組,找到可用的相機,并獲取相機的 cameraid
創(chuàng)建圖片接收器并進行訂閱,獲取 receiver 的 surfaceId
通過 CameraManager 的 createCameraInput(cameraid) 創(chuàng)建相機輸入流
通過 camera 的 createPreviewOutput(sufaceId) 創(chuàng)建相機預覽輸出流,這里 sufaceId 為 XComponent 的 id
通過 camera 的 createPhotoOutput(sufaceId) 創(chuàng)建相機拍照輸出流,這里 sufaceId 為圖片接收器的 surfaceId
會話管理:創(chuàng)建會話,并且配置會話的相機輸入流,相機拍照輸出流與相機預覽流,提交配置,開始會話
至此,相機就能正常的顯示出圖像了。
用拍照方法拍攝照片:
//拍攝照片
asynctakePicture(){
letphotoSettings={
rotation:camera.ImageRotation.ROTATION_0,
quality:camera.QualityLevel.QUALITY_LEVEL_LOW,
mirror:false
}
awaitthis.photoOutput.capture(photoSettings)
}
調(diào)用相機的輸出流的 capture 方法進行拍照操作,會觸發(fā)圖片接收器的監(jiān)聽,進行對字節(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()
}
釋放相機:
//結束釋放相機資源
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(()=>{
})
}
//釋放會話
if(this.captureSession){
awaitthis.captureSession.release((err)=>{
if(err){
console.error('zmwFailedtoreleasetheCaptureSessioninstance${err.message}');
return;
}
});
}
}
在完成了相機的調(diào)用后,需要對相機的資源進行釋放,否則再次調(diào)用的時候會一直被占用而導致黑屏。
總結
OpenHarmony 對于相機的官方使用文檔不太清晰,有許多的坑,需要去趟。
在這個過程中我遇到的問題:
在相機的使用時,由于開發(fā)板上的相機獲取到了兩個,一個是外接 USB 的相機,一個應該是系統(tǒng)的,在獲取相機的 id 的時候需要注意。
在保存相機拍照的圖片的時候,保存到沙箱路徑時顯示不到頁面上,需要在保存的路徑前加上"file://"。
需要擴展研究的是進行相機的攝像操作,以及相機拍照與攝像的切換操作。
-
相機
+關注
關注
5文章
1524瀏覽量
55302 -
Module
+關注
關注
0文章
74瀏覽量
13464 -
SDK
+關注
關注
3文章
1092瀏覽量
50943 -
鴻蒙
+關注
關注
60文章
2775瀏覽量
45201 -
OpenHarmony
+關注
關注
31文章
3905瀏覽量
20587
原文標題:鴻蒙上成功調(diào)用相機!
文章出處:【微信號:gh_834c4b3d87fe,微信公眾號:OpenHarmony技術社區(qū)】歡迎添加關注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
STM32上成功移植了emWim支持觸摸屏,但是效果不好?
如何在FPGA板上成功運行VHDL程序?
鴻蒙系統(tǒng)更新后新增相機多機位模式
在STM32F103ZE的一款開發(fā)板上成功移植uCOSIII
請問鴻蒙hap包是否支持插件化開發(fā)?
在鴻蒙上使用Python進行物聯(lián)網(wǎng)編程

鴻蒙上成功調(diào)用相機!
評論