読者です 読者をやめる 読者になる 読者になる

あんまり見ないでください

プログラミング・技術関連,アイディア,気づいたことなどを低レベルで垂れ流す場所.

WebRTCのgetUserMediaで取得した映像をいじってみた

WebRTCのgetUserMediaで取得した自分のカメラ映像の画素を取得し、いろいろいじってみた。

テンプレート

まず、いろいろいじるためのテンプレートを載せる。

template.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript" src="template.js"></script>
<title>Title</title>
</head>
<body>
<video id="video"></video>
<canvas id="display_canvas"></canvas>
</body>
</html>

template.js

navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia ;
window.URL = window.URL || window.webkitURL ;

function initialize() {
	navigator.getUserMedia(
		{audio: true, video: true},
		function(stream) {
			var video = document.getElementById('video');
			video.src = URL.createObjectURL(stream);
			video.play();
			renderStart();
		},
		function(error) {
			console.error(error);
		}
	);
}

function renderStart() {
	var video = document.getElementById('video');
	var buffer = document.createElement('canvas');
	var display = document.getElementById('display_canvas');
	var bufferContext = buffer.getContext('2d');
	var displayContext = display.getContext('2d');
	
	var render = function() {
		requestAnimationFrame(render);
		var width = video.videoWidth;
		var height = video.videoHeight;
		if (width == 0 || height == 0) {return;}
		buffer.width = display.width = width;
		buffer.height = display.height = height;
		bufferContext.drawImage(video, 0, 0);
		
		var src = bufferContext.getImageData(0, 0, width, height); // カメラ画像のデータ
		var dest = bufferContext.createImageData(buffer.width, buffer.height); // 空のデータ(サイズはカメラ画像と一緒)
		
		/* ##############################
		    ここで処理を行う
		############################## */

		displayContext.putImageData(dest, 0, 0);
	};
	render();
}

window.addEventListener('load', initialize);

src.data は、
src.data = { [画素(0)のR,G,B,alpha], [画素(1)のR,G,B,alpha], ... , [画素(width*height-1)のR,G,B,alpha] }
というふうにカメラ画像のRGB値とalpha値が入った長さ width*height*4 の配列になっている。(見やすくするために [ 画素(1)のR,G,B,alpha ] と表記しているが、実際は一次元配列。)
この元データを使って、dest.data に加工後の画素値を入れていく。

色調反転

まずは、単純に色調反転をしてみる。

ソースコード(処理と、その前後だけ)

var src = bufferContext.getImageData(0, 0, width, height); // カメラ画像のデータ
var dest = bufferContext.createImageData(buffer.width, buffer.height); // 空のデータ(サイズはカメラ画像と一緒)

for (var i = 0; i < dest.data.length; i += 4) {
	dest.data[i + 0] = 255 - src.data[i + 0]; // Red
	dest.data[i + 1] = 255 - src.data[i + 1]; // Green
	dest.data[i + 2] = 255 - src.data[i + 2]; // Blue
	dest.data[i + 3] = 255;                     // Alpha
}

displayContext.putImageData(dest, 0, 0);

こんな感じになる。不健康ですね。

f:id:artak:20140811192949p:plain

エッジ検出

http://www.html5.jp/canvas/ref/method/getImageData.html
にエッジ検出のサンプルがあったので、使ってみた。

ソースコード(処理と、その前後だけ)

var src = bufferContext.getImageData(0, 0, width, height); // カメラ画像のデータ
var dest = bufferContext.createImageData(buffer.width, buffer.height); // 空のデータ(サイズはカメラ画像と一緒)

for (var y = 1; y < height-1; y += 1) {
	for (var x = 1; x < width-1; x += 1) {
		for (var c = 0; c < 3; c += 1) {
			var i = (y*width + x)*4 + c;
			dest.data[i] = 127 + -src.data[i - width*4 - 4] -   src.data[i - width*4] - src.data[i - width*4 + 4] +
							-src.data[i - 4]       + 8*src.data[i]       - src.data[i + 4] +
							-src.data[i + width*4 - 4] -   src.data[i + width*4] - src.data[i + width*4 + 4];
		}
		dest.data[(y*width + x)*4 + 3] = 255; // Alpha
	}
}

displayContext.putImageData(dest, 0, 0);

こんな感じになる。動くと楽しい。

f:id:artak:20140811193953p:plain

おまけ

RGB値が 128 より小さい場合は 0 に、128 以上の場合は 255 に、という処理をしてみた。

ソースコード(処理と、その前後だけ)

var src = bufferContext.getImageData(0, 0, width, height); // カメラ画像のデータ
var dest = bufferContext.createImageData(buffer.width, buffer.height); // 空のデータ(サイズはカメラ画像と一緒)

for (var i = 0; i < dest.data.length; i += 4) {
	for (var c = 0; c < 3; c ++) {
		if (src.data[i+c] < 128) {
			dest.data[i+c] = 0;
		} else {
			dest.data[i+c] = 255;
		}
	}
	dest.data[i+3] = 255; // Alpha
}

displayContext.putImageData(dest, 0, 0);

こんな感じになる。なんかかっこいい。

f:id:artak:20140811194828p:plain

感想

カメラの画素を取得していじるのはOpenCVでやったことがあるが、その時は結構面倒なイメージだった。しかし、今回はJavaScriptのWebAPIを使うことで非常に楽に出来たので驚いた。