
シェア:
Vonageの元デベロッパー・アドボケイトで、ロンドンの地元テック・コミュニティをサポートする役割を担っていた。彼は経験豊富なイベントオーガナイザーであり、ボードゲーマーであり、ムーというかわいい犬のパパでもある。また、You Got This(幸せで健康的な仕事生活に必要なコア・スキルに関するイベント・ネットワーク)のリード・オーガナイザーでもある。
VonageビデオでJavascriptのグリーンスクリーンを使用する
所要時間:1 分
Vonage Video パブリッシャを作成する場合、ストリームは直接ユーザーカメラから、あるいは <video>要素、または HTML の <canvas>要素から直接取得できます。ピクセルがキャンバスに描画されると、Video API セッションで使用する前に簡単に操作できます。
このチュートリアルでは、グリーンスクリーンを削除し、ビデオ通話に含めることができる新しいカスタム画像に置き換える方法を学びます。
Overview of the project components
プロジェクトを成功させるには、いくつかの要素が必要だ。まず <video>要素がユーザーのカメラからストリームを受け取ります。各フレームの Video 要素のコンテンツはキャンバスに描画され、そこでピクセルをループ処理して緑色のピクセルを取り除きます。2 番目のキャンバスに、背景画像を置き換えて描画し、1 番目のキャンバスの緑以外のピクセルを重ねます。
キャンバスに希望の出力があれば、キャンバスを Vonage Video API パブリッシャーのソースとして使用し、友人との Video セッションで使用することができます。
完成したコードをご覧になりたい方は、以下をご覧ください。 https://github.com/nexmo-community/video-green-screen
足場のマークアップ
新しいプロジェクト・フォルダーを作成し、新しいファイル index.htmlこのファイルに以下のコードを入力する:
<!DOCTYPE html>
<html>
<head></head>
<body>
<video id="v1" width="320" height="240" autoplay></video>
<canvas id="c1" width="320" height="240"></canvas>
<canvas id="c2" width="320" height="240"></canvas>
<div id="opentok-publishers"></div>
<div id="opentok-subscribers"></div>
<script>
// Create references to the video and canvas elements
const v1 = document.getElementById('v1')
const c1 = document.getElementById('c1')
const c2 = document.getElementById('c2')
// Get canvas context
const c1Ctx = c1.getContext('2d')
const c2Ctx = c2.getContext('2d')
</script>
</body>
</html>
また、プロジェクトフォルダ内にグリーンスクリーンを置き換える画像が必要です。このチュートリアルでは Vonageブランドのグラデーション.キャンバスのコンテキストを取得したら、画像を読み込みます:
const backgroundImage = new Image()
backgroundImage.src = 'vonage-gradient.png' ウェブカメラビデオ
要素のソースを <video>要素のソースをユーザーのウェブカメラからのストリームに設定します。このスニペットはデフォルトのカメラを選択します:
navigator.mediaDevices.getUserMedia({ video: true })
.then(stream => { v1.srcObject = stream })
空の process()関数を作成する。ユーザーのVideoデバイスが準備できて「再生」したら、関数を毎フレーム実行する:
v1.addEventListener('play', () => {
setInterval(process, 0)
})
function process() {
}
ビデオストリームをキャンバスに描画する
更新 process():
function process() {
c1Ctx.drawImage(v1, 0, 0, 320, 240)
c2Ctx.drawImage(backgroundImage, 0, 0, 320, 240)
}ページを更新してください。 <video>要素、最初の <canvas>要素が表示されるはずです。 <canvas>が表示されるはずです。目標は、2 番目のキャンバスの背景の上に緑以外のピクセルを配置することです。
ループ・スルー・ピクセル
キャンバスでは、画像全体がピクセルの1つの長い配列で表されます。320x240の画像には76,800個の配列があると思うかもしれませんが、それは間違いです。
canvas pixels
各可視ピクセルは、4つの配列項目で構成される。1つは赤の値、1つは緑の値、1つは青の値、そして最後の1つは不透明度を設定するためのものである。これらの値は、ループを構築し、使用する上で重要である。
このフレームのピクセル配列を process()関数の内部でこのフレームのピクセル配列を取得し、ループを構築します:
const frame = c1Ctx.getImageData(0, 0, 320, 240)
const pixels = frame.data
for(let i=0; i<pixels.length; i+=4) {
}このループが実行されるたびに、カウンタが4ずつ増加するように設定されていることに注意、 iは次の可視ピクセルの赤の値の配列インデックスになる。
HSLを理解する
緑色のピクセルを除去する前に、HSL(Hue Saturation Lightness:色相飽和明度)カラーフォーマットについて紹介したい。
Hue is a color wheel, sauturation is the amount of grey, lightness is a scale of black to white色相をカラーホイールのように考えて、ホイール上の位置を0から360の間で色を指定することができる。緑の "範囲 "は人によって違うかもしれないが、私の場合は90から200がうまくいく。
ただし、ピクセルを <canvas>にピクセルを読み書きする場合は、赤緑青(RGB)カラーフォーマットを使用する必要があります。あなたの <script>の一番下に RGBToHSL()関数 CSSトリック
function RGBToHSL(r, g, b) {
r /= 255; g /= 255; b /= 255;
let cmin = Math.min(r,g,b),
cmax = Math.max(r,g,b),
delta = cmax - cmin,
h = 0, s = 0, l = 0;
if (delta == 0) h = 0;
else if (cmax == r) h = ((g - b) / delta) % 6;
else if (cmax == g) h = (b - r) / delta + 2;
else h = (r - g) / delta + 4;
h = Math.round(h * 60);
if (h < 0) h += 360;
l = (cmax + cmin) / 2;
s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
s = +(s * 100).toFixed(1);
l = +(l * 100).toFixed(1);
return [h, s, l]
} 緑のピクセルを透明にする
ループの中で process()ループの中で、各ピクセルのRGB値とHSL値を取得し、緑色のピクセルを透明に設定する:
const [r, g, b] = [pixels[i], pixels[i+1], pixels[i+2]]
const [h, s, l] = RGBToHSL(r, g, b)
if(h > 90 && h < 200) {
pixels[i+3] = 0
}ループの後、キャンバスの画像を更新する:
frame.data = pixels
c1Ctx.putImageData(frame, 0, 0)を見つけるかもしれない。 90と 200を更新する必要があるかもしれません。
The first canvas has no background - appearing white
置換された背景に残りのピクセルを描く
1つ目のキャンバスに残っている各ピクセルについて、2つ目のキャンバスに描画する。の if文の後に process()ループの文の後に else条件を追加します:
if(h > 90 && h < 200) {
pixels[i+3] = 0
} else {
c2Ctx.fillStyle = `rgba(${r}, ${g}, ${b}, 1)`
const x = (i/4) % 320
const y = Math.floor((i / 4) / 320)
c2Ctx.fillRect(x, y, 1, 1)
}その xと yの値は視覚的なピクセルなので iの値は4で割らなければならない。
The second canvas now has the non-removed pixels
Video API セッションにキャンバスを含める
で新しいプロジェクトを作成します。 ビデオダッシュボードで新しいプロジェクトを作成します。.作成後、Project Toolsまでスクロールダウンし、新しいRoutedセッションを作成します。セッションIDを取得し、新しいトークンを作成します。
プロジェクトのトップに <script>の一番上に、プロジェクト・ダッシュボードからのデータで3つの新しい変数を作成します:
const sessionId = 'YOUR_SESSION_ID'
const apiKey = 'YOUR_PROJECT_API_KEY'
const token = 'YOUR_TOKEN'次に <script>タグを Vonage Video API Client SDKページからタグをコピーします。ページからタグをコピーし、既存の <script>タグの上に置きます。
タグの一番下にある <script>タグの下部で、基本的な Video API セッションを初期化し、2 番目のキャンバスから公開します:
// Initialize session
const session = OT.initSession(apiKey, sessionId)
// Create publisher
const publisher = OT.initPublisher("opentok-publishers", {
videoSource: c2.captureStream().getVideoTracks()[0],
width: 320,
height: 240
})
// Once connected to session, publish the publisher
session.connect(token, () => {
session.publish(publisher)
})
// Show other users' streams
session.on('streamCreated', event => {
session.subscribe(event.stream, "opentok-subscribers")
}) 要素を隠す
その <video>要素と <canvas>要素はこの機能を実現するために必要だが、おそらくウェブページでは表示したくないだろう。あなたの <head>で、それらを隠すために以下のCSSを追加してください:
<style>
#v1, #c1, #c2 { display: none }
</style> あなたの経歴は?
このブログ記事がお役に立ち、カスタム背景を思う存分作成できることを願っています。今回はグリーンスクリーンに焦点を当てましたが、どのようなピクセルレベルの操作も同じアプローチで行うことができます。
これをさらに進めるには、置き換えられる「範囲内」のHSL値を変更するコントロールや、画像を変更するファイルセレクタをユーザーに提供することもできます。
最終プロジェクトは下記でご覧いただけます。 https://github.com/nexmo-community/video-green-screen
これまで通り、サポートが必要な場合はお気軽に Vonage開発者コミュニティSlack.そこでお会いできることを楽しみにしています。