利用 ImageData 实现图片左右旋转 90°
问题描述
最近在做 OCR
图像识别。大致流程是先拿到预览区图片的 base64
字符串,根据接口要求压缩 base64
字符串大小,再调用 OCR
相关接口获取识别结果。然而,通过文件上传 input
域、FileReader
读到的 base64
字符串直接放入 img
标签后,预览出的图片往往会出现 原本为纵向拍摄的图片默认按横向图片展示,从而导致后续 OCR
识别报错。
方向正确:
方向错误:
究其原因,可能是 canvas.toDataURL(type, quality)
生成的 base64
字符串没有图片朝向相关的标识,赋给 img.src
后,img
标签 默认将较长的一边作为宽度、较短的一边作为高度 来显示图片。要解决这个问题,需要在图片加载完毕后,手动调节图片的朝向(右转或左转 90°)。
网上关于图片转向的文章大多通过构造新的 canvas
画布,设置具体的转向角度后重绘图片,最后在写回原图片。结合项目实际需求,只需要简单左右旋转 90° 即可。这可以通过 ImageData
对象的像素变换轻松实现。
基本原理1——像素矩阵变换
ImageData
是图片经数据化处理后的对象,其中包含三个属性:
width
:图片的总宽度像素值(整数)height
:图片的总高度像素值(整数)data
:八位无符号整型固定数组、一个特殊的类型数组。该数组每 4 个元素的值,依次描述了对应像素点的 R、G、B、A 的取值,值域均为 [0, 255]。
因此一个 4 × 3
像素的原始图片,可以看作如下形式的像素矩阵 A:
A = [ a 11 a 12 a 13 a 14 a 21 a 22 a 23 a 24 a 31 a 32 a 33 a 34 ] (1) A = \left[ \begin{matrix} a_{11} & a_{12} & a_{13} & a_{14} \\ a_{21} & a_{22} & a_{23} & a_{24} \\ a_{31} & a_{32} & a_{33} & a_{34} \end{matrix} \right] \tag{1} A=⎣⎡a11a21a31a12a22a32a13a23a33a14a24a34⎦⎤(1)
图片向右旋转 90°,实质就是设法将 A 变为 A’ ——
A ′ = [ a 31 a 21 a 11 a 32 a 22 a 12 a 33 a 23 a 13 a 34 a 24 a 14 ] (2) A'= \left[ \begin{matrix} a_{31} & a_{21} & a_{11}\\ a_{32} & a_{22} & a_{12}\\ a_{33} & a_{23} & a_{13}\\ a_{34} & a_{24} & a_{14} \end{matrix} \right] \tag{2} A′=⎣⎢⎢⎡a31a32a33a34a21a22a23a24a11a12a13a14⎦⎥⎥⎤(2)
这可以通过原矩阵一次 转置、与多次初等 列 变换(逆序排列各列)得到:
A T = [ a 11 a 12 a 13 a 14 a 21 a 22 a 23 a 24 a 31 a 32 a 33 a 34 ] T = [ a 11 a 21 a 31 a 12 a 22 a 32 a 13 a 23 a 33 a 14 a 24 a 34 ] = > [ a 31 a 21 a 11 a 32 a 22 a 12 a 33 a 23 a 13 a 34 a 24 a 14 ] = A ′ (3) A^T=\left[ \begin{matrix} a_{11} & a_{12} & a_{13} & a_{14} \\ a_{21} & a_{22} & a_{23} & a_{24} \\ a_{31} & a_{32} & a_{33} & a_{34} \end{matrix} \right]^T= \left[ \begin{matrix} a_{11} & a_{21} & a_{31}\\ a_{12} & a_{22} & a_{32}\\ a_{13} & a_{23} & a_{33}\\ a_{14} & a_{24} & a_{34} \end{matrix} \right] => \left[ \begin{matrix} a_{31} & a_{21} & a_{11}\\ a_{32} & a_{22} & a_{12}\\ a_{33} & a_{23} & a_{13}\\ a_{34} & a_{24} & a_{14} \end{matrix} \right] = A' \tag{3} AT=⎣⎡a11a21a31a12a22a32a13a23a33a14a24a34⎦⎤T=⎣⎢⎢⎡a11a12a13a14a21a22a23a24a31a32a33a34⎦⎥⎥⎤=>⎣⎢⎢⎡a31a32a33a34a21a22a23a24a11a12a13a14⎦⎥⎥⎤=A′(3)
同理,图片向左旋转 90°,实际上就是得到矩阵 A’' :
A ′ ′ = [ a 14 a 24 a 34 a 13 a 23 a 33 a 12 a 22 a 32 a 11 a 21 a 31 ] (4) A''= \left[ \begin{matrix} a_{14} & a_{24} & a_{34}\\ a_{13} & a_{23} & a_{33}\\ a_{12} & a_{22} & a_{32}\\ a_{11} & a_{21} & a_{31} \end{matrix} \right] \tag{4} A′′=⎣⎢⎢⎡a14a13a12a11a24a23a22a21a34a33a32a31⎦⎥⎥⎤(4)
这可以通过原矩阵一次 转置、与多次初等 行 变换(逆序排列各行)得到——
A T = [ a 11 a 12 a 13 a 14 a 21 a 22 a 23 a 24 a 31 a 32 a 33 a 34 ] T = [ a 11 a 21 a 31 a 12 a 22 a 32 a 13 a 23 a 33 a 14 a 24 a 34 ] = > [ a 14 a 24 a 34 a 13 a 23 a 33 a 12 a 22 a 32 a 11 a 21 a 31 ] = A ′ ′ (5) A^T = \left[ \begin{matrix} a_{11} & a_{12} & a_{13} & a_{14} \\ a_{21} & a_{22} & a_{23} & a_{24} \\ a_{31} & a_{32} & a_{33} & a_{34} \end{matrix} \right]^T= \left[ \begin{matrix} a_{11} & a_{21} & a_{31}\\ a_{12} & a_{22} & a_{32}\\ a_{13} & a_{23} & a_{33}\\ a_{14} & a_{24} & a_{34} \end{matrix} \right]=> \left[ \begin{matrix} a_{14} & a_{24} & a_{34}\\ a_{13} & a_{23} & a_{33}\\ a_{12} & a_{22} & a_{32}\\ a_{11} & a_{21} & a_{31} \end{matrix} \right] = A'' \tag{5} AT=⎣⎡a11a21a31a12a22a32a13a23a33a14a24a34⎦⎤T=⎣⎢⎢⎡a11a12a13a14a21a22a23a24a31a32a33a34⎦⎥⎥⎤=>⎣⎢⎢⎡a14a13a12a11a24a23a22a21a34a33a32a31⎦⎥⎥⎤=A′′(5)
基本原理2——像素数组与矩阵的对应关系
由于 ImageData.data
对应一个数组,对于 4 × 3
的图片而言,ImageData.data
就是一个具有 48 个元素的数组 D,不妨每个元素的值就是其下标值,则:
D = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7...44 , 45 , 46 , 47 ] (6) D = \left[0, 1, 2, 3, 4, 5, 6, 7... 44, 45, 46, 47\right]\tag{6} D=[0,1,2,3,4,5,6,7...44,45,46,47](6)
其中:
元组 (0, 1, 2, 3)
表示第 1(= 0 / 4 + 1)
个像素的颜色为 rgba(0, 1, 2, 3/255)
;
元组 (4, 5, 6, 7)
表示第 2(= 4 / 4 + 1)
个像素的颜色为 rgba(4, 5, 6, 7/255)
;
元组 (8, 9, 10, 11)
表示第 3(= 8 / 4 + 1)
个像素的颜色为 rgba(8, 9, 10, 11/255)
;
…
元组 (i, i+1, i+2, i+3)
表示第 (i / 4 + 1)
个像素的颜色为 rgba(i, i+1, i+2, (i+3)/255)
;
…
元组 (44, 45, 46, 47)
表示第 12(= 44 / 4 + 1)
个像素的颜色为 rgba(44, 45, 46, 47/255)
。
可见从 0 开始遍历 D 数组,每次递增 4 个单位,即可依次得到各个像素的红色值 R,再依次加1、加2、加3,即得到对应的绿色值 G、蓝色值 B、等效 α 通道值 A。
反之,如果知道图片的像素尺寸为 4 × 3
,则可以通过下图找到数组 D 的各个元素:
可见各像素点是按照 从左至右、从上至下 的顺序排列的。设图片总宽度像素为 W,总高度像素为 H,任一像素点 P 的坐标为 (x, y)
,P 的红色值在数组 D 的下标为 R(x, y)
,则:
R ( x , y ) = ( x + W ⋅ y ) × 4 (7) R(x, y) = (x + W · y) × 4 \tag{7} R(x,y)=(x+W⋅y)×4(7)
验证:(x 与 y 均从 0 开始计数)
R(2, 1) = (2 + 1 × 4) × 4 = 24
R(1, 2) = (1 + 2 × 4) × 4 = 36
R(3, 1) = (3 + 1 × 4) × 4 = 28
拿到了 R(x, y)
,不难求出该像素的纵向中心对称像素 Rh(x, y)
、横向中心对称像素 Rw(x, y)
、以及主对角线对称像素 Rd(x, y)
:
R h ( x , y ) = [ x + W ⋅ ( H − 1 − y ) ] × 4 (8-1) Rh(x, y) = [x + W · (H - 1 - y)] × 4 \tag{8-1} Rh(x,y)=[x+W⋅(H−1−y)]×4(8-1)
R w ( x , y ) = [ ( W − 1 − x ) + W ⋅ y ] × 4 (8-2) Rw(x, y) = [(W - 1 - x) + W · y] × 4 \tag{8-2} Rw(x,y)=[(W−1−x)+W⋅y]×4(8-2)
R d ( x , y ) = ( y + H ⋅ x ) × 4 (8-3) Rd(x, y) = (y + H · x) × 4 \tag{8-3} Rd(x,y)=(y+H⋅x)×4(8-3)
其中,式(8-3)用于 转置 运算;式(8-1)、式(8-2)分别用于 初等行变换 及 初等列变换。
具体实现
基本思路:
- 通过
canvas
获取目标图片的ImageData
对象; - 转置原图片数组,得到数组 AT;
- 对 AT 执行一组初等行变换,使各行逆序排列,得到左旋 90° 效果;
- 对 AT 执行一组初等列变换,使各列逆序排列,得到右旋 90° 效果;
- 将新的像素数组写回图片源标签。
HTML
:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Rotate by ImageData</title><style>.image{ margin-top: 5px; }</style>
</head>
<body><div class="btns"><input type="button" value="左转 90°" id="turnLeft" /><input type="button" value="右转 90°" id="turnRight" /></div><div class="image"><img id="fruit" src="fruit.jpg" class="image" alt="fruit" title="fruit" /></div><script src="data:imageRotate.js"></script>
</body></html>
imageRotate.js
:
document.querySelector('#turnLeft' ).addEventListener('click', e => rotateImage('l'))
document.querySelector('#turnRight').addEventListener('click', e => rotateImage('r'))function rotateImage(direction = 'l') {// 1. Prepare ImageDatalet img = document.querySelector('#fruit')const { width: W, height: H } = imglet cvs = document.createElement('canvas')cvs.width = Wcvs.height = Hlet ctx = cvs.getContext('2d')ctx.drawImage(img, 0, 0)let imgDt0 = ctx.getImageData(0, 0, W, H)let imgDt1 = new ImageData(H, W)let imgDt2 = new ImageData(H, W)let dt0 = imgDt0.datalet dt1 = imgDt1.datalet dt2 = imgDt2.data// 2. Transposelet r = r1 = 0 // index of red pixel in old and new ImageData, respectivelyfor (let y = 0, lenH = H; y < lenH; y++) {for (let x = 0, lenW = W; x < lenW; x++) {r = (x + lenW * y) * 4r1 = (y + lenH * x) * 4dt1[r1 + 0] = dt0[r + 0]dt1[r1 + 1] = dt0[r + 1]dt1[r1 + 2] = dt0[r + 2]dt1[r1 + 3] = dt0[r + 3]}}// 3. Reverse width / heightfor (let y = 0, lenH = W; y < lenH; y++) {for (let x = 0, lenW = H; x < lenW; x++) {r = (x + lenW * y) * 4r1 = direction === 'l'? (x + lenW * (lenH - 1 - y)) * 4: ((lenW - 1 - x) + lenW * y) * 4dt2[r1 + 0] = dt1[r + 0]dt2[r1 + 1] = dt1[r + 1]dt2[r1 + 2] = dt1[r + 2]dt2[r1 + 3] = dt1[r + 3]}}// 4. Redraw imagecvs.width = Hcvs.height = Wctx.clearRect(0, 0, W, H)ctx.putImageData(imgDt2, 0, 0, 0, 0, H, W)img.src = cvs.toDataURL('image/jpeg', 1)
}
运行结果:
原始图片:
左转 90°:
右转 90°:
示例文件
链接: https://pan.baidu.com/s/1w1_5qh3Tg95VUUjLmhrvTA
提取码: v7f6
利用 ImageData 实现图片左右旋转 90°相关推荐
- ios 拍照上传到服务器_ios端浏览器拍照上传到服务器,图片被旋转90度 php 解决方案...
1.可以通过前端进行解决,本案例通过后端解决的 判断请求的浏览器的ua,如果是ios浏览器则进行90度旋转 重点来了: 必须确保检测的图片是ios设备上传的完整图片,不要在前端压缩过的,因为压缩后的图 ...
- 上传图片的时候,ios手机的图片会旋转90°
1.问题:在html5中利用canvas对上传图片压缩的时候,ios手机竖着拍照时,图片会旋转90°,其他情况正常. 2.解决方法:获取拍照角度,对Ios竖着拍的照片进行角度处理 3.利用exif.j ...
- python图片旋转脚本_Python+OpenCV 实现图片无损旋转90°且无黑边
0. 引言 有如上一张图片,在以往的图像旋转处理中,往往得到如图所示的图片. 然而,在进行一些其他图像处理或者图像展示时,黑边带来了一些不便.本文解决图片旋转后出现黑边的问题,实现了图片尺寸不变的旋转 ...
- 解决H5 IOS手机图片上传时图片会旋转90°问题
解决H5 IOS手机图片上传时图片会旋转90°问题 Vant 官方给出的解答需要自己解决,没有处理. 解决办法主要使用了 compressorjs 插件库 一.Vant UI库Uploader 组件图 ...
- 解决ios横屏拍照图片自动旋转90度问题
解决ios横屏拍照图片自动旋转90度问题 参考文章: (1)解决ios横屏拍照图片自动旋转90度问题 (2)https://www.cnblogs.com/lanshengzhong/p/900856 ...
- iOS开发- 相机(摄像头)获取到的图片自动旋转90度解决办法
http://www.tuicool.com/articles/IfEZre 今天写demo的时候发现, 如果把通过相机获取到的图片,直接进行操作, 比如裁剪, 缩放, 则会把原图片向又旋转90度. ...
- android 竖屏拍照旋转90度,三星等机型上拍照后图片被旋转90度的解决方案
考虑到Android7.0以后拍照修改了调用和返回方式,找到了一个看起来还不错的第三方库,实际可能并非如此. -TakePhoto 在三星Note3和S6上测试,发现竖屏拍照后返回的照片是横屏的,在其 ...
- html中如何使图片自动旋转90度,css怎么让图片旋转90度?
css怎么让图片旋转90度?下面本篇文章给大家介绍一下使用CSS让图片旋转90度的方法.有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助. css怎么让图片旋转90度? 在CSS中,可以 ...
- html中如何使图片自动旋转90度,css实现图片旋转90度的方法
css实现图片旋转90度的方法 发布时间:2020-08-31 11:44:39 来源:亿速云 阅读:550 作者:小新 小编给大家分享一下css实现图片旋转90度的方法,相信大部分人都还不怎么了解, ...
最新文章
- react项目---基本语法字符串数组(6)
- PHP 学习笔记 01
- Android之系统自带的文字外观设置
- Ubuntu下pip安装、升级、卸载
- 关于子对话框的创建与销毁
- gan简介_GAN简介
- Educational Codeforces Round 111 (Rated for Div. 2)
- Java EE 7中的WebSocket客户端API
- 恋爱Linux(Fedora20)2——安装Java运行环境(JDK)
- 亿嘉和机器人上市了吗_亿嘉和上半年收入持续增长,拟7亿元定增加码主业研发...
- Kubernetes学习总结(8)—— Kubernetes Pod 资源管理 和 Pod 服务质量
- Spyder IDE中使用git
- always on sql 收缩日志_sql server日志文件过大无法收缩的问题
- 以Crypto++实现RSA加解密二进制数据
- python下载网页内容_使用selenium下载整个html页面内容
- 如何查询redhat的版本信息
- JavaScript常见的网页特效(元素样式相关属性)
- Microsoft.NET离线运行库合集
- 使用 jsonp解决跨域问题,在vue中成功调用心知天气api
- SPJ数据库—初识sql语句(02)(注释版)