引言 最近有不少開發者向我們諮詢,像體測、賽事等應用場景中,需要儲存運動過程的影像,如何將相機抽取的RGBA幀影像解析成
.jpg
或.png
格式的影像?今天我們就為您介紹相應的解決方案。
一、RGBA影像結構。
RGBA影像為一維陣列,每四個元素描寫一個影像畫素,前三元素為顏色值,第四個元素為透明度值,按列掃描順序分佈,如下圖所示,為一個寬2px、高3px的結果,如下圖所示:
二、相關API。
根據上面的影像結構,我們把相應的畫素點在canvas上畫出來即可。為了減少繪製時對介面的干擾,我們使用uni.createOffsetScreen()建立離屏畫布繪製。
三、程式碼實現。
實現程式碼如下,抽幀程式碼見前面的抽幀章節。
...
onCameraReady(e) {
const context = wx.createCameraContext();
const listener = context.onCameraFrame(frame => {
this.parseToJpeg(frame);
});
listener.start();
}
parseToJpeg(){
const canvas = uni.createOffscreenCanvas({
width: frame.width,
height: frame.height,
type: '2d'
});
let offset = 0;
let gl = canvas.getContext('2d');
const buffer = frame.data;
for (let y = 0; y < frame.height; y++) {
for (let x = 0; x < frame.width; x++) {
let r = buffer[offset].toString(16).padStart(2, '0');
let g = buffer[offset + 1].toString(16).padStart(2, '0');
let b = buffer[offset + 2].toString(16).padStart(2, '0');
let a = buffer[offset + 3].toString(16).padStart(2, '0');
let color = `#${r}${g}${b}${a}`;
//console.log(color, offset);
gl.fillStyle = color;
gl.fillRect(x, y, 1, 1);
offset += 4;
}
}
}
...
四、後記
4.1、為什麼不直接使用拍照API?
這主要出於兩個原因,一是在抽幀的同時無法呼叫
CameraContext.takePhoto()
API進行拍照; 二是由於抽幀是非同步的,所以無法同步呼叫,可能會導致識別時機的影像與拍攝的一不致。
4.2、影像轉換呼叫頻率。
由於影像轉換是比較消耗計算資源的,所以建議不要在抽幀時同步呼叫影像轉換,建議先暫存要轉換的幀,等運動結束是再進行轉換、上傳等操作。