<!DOCTYPE html>
<html xmlns="">
<head>
<title>HTML5 GetUserMedia Demo</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
</head>
<body>
<input type="button" title="开启摄像头" value="开启摄像头" onclick="getMedia()" /><br />
<video height="120px" autoplay="autoplay"></video><hr />
<input type="button" title="拍照" value="拍照" onclick="getPhoto()" /><br />
<canvas id="canvas1" height="120px" ></canvas><hr />
<input type="button" title="视频" value="视频" onclick="getVedio()" /><br />
<canvas id="canvas2" height="120px"></canvas>
<script type="text/javascript">
var video = document.querySelector('video')
var audio, audioType
var canvas1 = document.getElementById('canvas1')
var context1 = canvas1.getContext('2d')
var canvas2 = document.getElementById('canvas2')
var context2 = canvas2.getContext('2d')
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia
window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL
var exArray = []//存储设备源ID
MediaStreamTrack.getSources(function (sourceInfos) {
for (var i = 0i != sourceInfos.length++i) {
var sourceInfo = sourceInfos[i]
//这里会遍历audio,video,所以要加以区分
if (sourceInfo.kind === 'video') {
exArray.push(sourceInfo.id)
}
}
})
function getMedia() {
if (navigator.getUserMedia) {
navigator.getUserMedia({
'video': {
'optional': [{
'sourceId': exArray[1] //0为前置摄像头,1为后置
}]
},
'audio':true
}, successFunc, errorFunc) //success是获取成功的回调函数
}
else {
alert('Native device media streaming (getUserMedia) not supported in this browser.')
}
}
function successFunc(stream) {
//alert('Succeed to get media!')
if (video.mozSrcObject !== undefined) {
//Firefox中,video.mozSrcObject最初为null,而不是未定义的,我们可以靠这个来检测Firefox的支持
video.mozSrcObject = stream
}
else {
video.src = window.URL &&window.URL.createObjectURL(stream) || stream
}
//video.play()
// 音频
audio = new Audio()
audioType = getAudioType(audio)
if (audioType) {
audio.src = 'polaroid.' + audioType
audio.play()
}
}
function errorFunc(e) {
alert('Error!'+e)
}
// 将视频帧绘制到Canvas对象上,Canvas每60ms切换帧,形成肉眼视频效果
function drawVideoAtCanvas(video,context) {
window.setInterval(function () {
context.drawImage(video, 0, 0,90,120)
}, 60)
}
//获取音频格式
function getAudioType(element) {
if (element.canPlayType) {
if (element.canPlayType('audio/mp4codecs="mp4a.40.5"') !== '') {
return ('aac')
} else if (element.canPlayType('audio/oggcodecs="vorbis"') !== '') {
return ("ogg")
}
}
return false
}
// vedio播放时触发,绘制vedio帧图像到canvas
//video.addEventListener('play', function () {
//drawVideoAtCanvas(video, context2)
//}, false)
//拍照
function getPhoto() {
context1.drawImage(video, 0, 0,90,120)//将video对象内指定的区域捕捉绘制到画布上指定的区域,实现拍照。
}
//视频
function getVedio() {
drawVideoAtCanvas(video, context2)
}
</script>
</body>
</html>
HTML5 The Media Capture API提供了对摄像头的可编程访问,用户可以直接用getUserMedia获得摄像头提供的视频流。但实际上用html5调用手机摄像头存在很多问题:
1)谷歌的发布的Chrome到了21版本后,才新增了一个用于高质量视频音频通讯的getUserMedia API,该API允许Web应用程序访问摄像头和麦克风,其他手机浏览器只有opera支持html5调用本地拍照功能
2)两个浏览器均不支持访问多个摄像头:chrome不支持访问后置摄像头,pera支持访问后置摄像头的
它的主要功能就是上传两张人像,通过算法进行分析对比,最后得出一个相似度的分数,以验证你们是天造地设还是颜值互补。但是,当我们把上传的图片转换成base64格式,发送给后台时,会发现偶尔会出现问题,有一些图片本来是这样的:
柴犬
处理之后却变成了这样:
柴犬2
经过测试发现,只有iOS手机竖着拍的照片才会出现这样的问题,而iOS手机横着拍的照片、Android手机拍的照片以及通过屏幕截图、网络下载等途径获得的图片都不会产生这个问题。
那么,这到底是为什么呢?
在开发过程中,由于时间紧迫,未求甚解,使用了github上的一个开源项目 lrz.js 来解决此问题,这个工具的主要用途是在尽量保证图片质量的前提下压缩图片的大小,但同时也附带了图片旋转角度纠正的功能。
通过阅读 lrz.js 的源代码,我发现它引入了一个叫做 exif.js 的库来实现旋转角度的纠正,它提供了js读取图像的原始数据的功能扩展,例如:拍照方向、相机设备型号、拍摄时间、ISO 感光度、GPS 地理位置等数据。而拍照方向就是关键所在!
exif.js 获取图像的拍照方向的代码如下:
EXIF.getData(IMG_FILE, function () { // IMG_FILE为图像数据
var orientation = EXIF.getTag(this, "Orientation")
console.log("Orientation:" + orientation)// 拍照方向
})
获取拍照方向的结果为1-8的数字:
拍照方向信息
注意:对于上面的八种方向中,加了*的并不常见,因为它们代表的是镜像方向,如果不做任何的处理,不管相机以任何角度拍摄,都无法出现镜像的情况。
这个表格代表什么意义?我们来看第一行,值为1时,右边两列的值分别为:Row #0 is Top,Column #0 is Left side,其实很好理解,它表示照片的第一行位于顶端,而第一列位于左侧,那么这张照片自然就是以正常角度拍摄的。
而这8种结果,就是第一行与第一列所在的位置的8种组合。
那么,我们来测试一下iOS手机横着拍的照片,来看看它的拍照方向是什么呢?
测试1
结果是1,即以正常角度拍摄的,其实也就是原图啦~
那么,我们再测试一下iOS手机竖着拍的照片,来看看它的拍照方向是什么呢?
测试2
原来是6!即第一行位于右侧,第一列位于顶端,其实相当于将照片顺时针旋转了90度!
所以,实际上iOS手机竖着拍出的照片与横着拍出的照片其本质上是一样的,只不过竖着拍出的照片被添加了一个顺时针旋转90°的拍照方向,所以显示的时候,就变成了上下边窄左右边宽的状态,其实也就是横着拍的照片顺时针旋转90°而成的~
那么明白了这些,文章开头所说的照片旋转bug的原因,也就很简单啦~
其实就是当我们在前端对图片进行像素处理或者drawInRect等 *** 作之后,照片的Orientaion信息,即为拍照方向信息被删除了,所以iOS手机竖着拍的照片又回到了横着的状态,看起来也就是逆时针旋转了90°!
那么如何纠正这个旋转角度呢?
其实思路也很简单:在处理图片之前,先读取并保存图片的拍照方向信息,然后在处理图片之后,再根据拍照方向,对图片进行相应的调整,lrz.js 中的代码如下:
switch (orientation) {
case 3:
ctx.rotate(180 * Math.PI / 180)
ctx.drawImage(img, -resize.width, -resize.height, resize.width, resize.height)
break
case 6:
ctx.rotate(90 * Math.PI / 180)
ctx.drawImage(img, 0, -resize.width, resize.height, resize.width)
break
case 8:
ctx.rotate(270 * Math.PI / 180)
ctx.drawImage(img, -resize.height, 0, resize.height, resize.width)
break
case 2:
ctx.translate(resize.width, 0)
ctx.scale(-1, 1)
ctx.drawImage(img, 0, 0, resize.width, resize.height)
break
case 4:
ctx.translate(resize.width, 0)
ctx.scale(-1, 1)
ctx.rotate(180 * Math.PI / 180)
ctx.drawImage(img, -resize.width, -resize.height, resize.width, resize.height)
break
case 5:
ctx.translate(resize.width, 0)
ctx.scale(-1, 1)
ctx.rotate(90 * Math.PI / 180)
ctx.drawImage(img, 0, -resize.width, resize.height, resize.width)
break
case 7:
ctx.translate(resize.width, 0)
ctx.scale(-1, 1)
ctx.rotate(270 * Math.PI / 180)
ctx.drawImage(img, -resize.height, 0, resize.height, resize.width)
break
default:
ctx.drawImage(img, 0, 0, resize.width,resize.height)
}
其中,translate是平移变换,scale(-1,1)是向左翻转,rotate是顺时针旋转。
举例说明 case 2,当图片的拍照方向为2时,即第一行位于顶端,而第一列位于右侧,其实相当于把照片进行了左右的翻转。所以,这里对图片的 *** 作是,先向右平移等于图片宽度的距离,再向左翻转,这相当于以图片水平方向的对称轴为轴进行了左右翻转,然后再以(0,0)为起始点绘制原宽高的图片,即完成了对拍照方向的纠正。
最后
经过一系列的测试,发现确实只有iOS手机的竖拍照片与横拍照片是通过拍照方向来区别的,Android手机无论竖拍还是横拍的照片,拍照方向都为1,也就是说即使丢失了拍照方向这一信息,也不会影响到图片的旋转角度。而手机或电脑的屏幕截图、网络上的图片、通过PS制作的图片等也是如此。
作者:任无名F
链接:http://www.jianshu.com/p/ad4501db178e
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)