图片像素分析与功能实现 您所在的位置:网站首页 生活中函数的例子图片 图片像素分析与功能实现

图片像素分析与功能实现

2023-03-23 08:15| 来源: 网络整理| 查看: 265

文章目录 rgba概念换肤功能旋转功能反相功能图片跨域

rgba概念

图片是由一个个像素点组成。每一个像素点包含四个值,决定了渲染出来的状态。这四个值为rgba(red, green, blue, alpha)。

前三个值是 红绿蓝,值的大小范围从 0到255 ,或者从 0%到100% 之间。

第四个值 alpha,规定了色彩的透明度,它的范围为0到1之间。其中0代表完全透明,1代表完全可见。通过getImageData方法得到的alpha值范围为0~255

红绿蓝是色彩中的三元色,通过设置这三种颜色所占的比重,可以变幻出其他所有颜色。

既然每个像素点可以通过rgba的值来表达,那么一张图片所包含的所有像素点都可以转换成数据。如果修改某部分像素点的rgba值,那该图片渲染出来的效果就会发生变化,这样便实现了图片的编辑。

换肤功能

我们通过一个换肤功能来理清具体过程。

首先我们定义一些全局信息

// 定义图像宽高 let imgWidth = null let imgHeight = null // 定义canvas画布 const canvas = document.createElement('canvas') const ctx = canvas.getContext('2d');

那怎么把图片转化成由像素点组成的数据呢?

首先编写一个getImageData函数将原始图片转化成数据(代码如下)。

图片转换成像素数据按以下两步操作。

调用ctx.drawImage(img, x, y, width, height) 用于创建ImageData对象调用ctx.getImageData(x, y, width, height) 用于从canvas中获取ImageData对象 /** * 获取图片像素点信息 * * @param { String } picPath 图片路径 * * @returns Promise **/ function getImageData(picPath) { const image = new Image(); image.src = picPath; return new Promise((resolve) => { image.onload = (e) => { imgWidth = e.target.width imgHeight = e.target.height canvas.width = imgWidth canvas.height = imgHeight ctx.drawImage(image, 0, 0, imgWidth, imgHeight); // 将图片绘制到画布上 const imgData = ctx.getImageData(0, 0, imgWidth, imgHeight); // 获取画布上的图像像素 resolve(imgData.data) // 获取到的数据为一维数组,包含图像的RGBA四个通道数据 ctx.clearRect(0, 0, imgWidth, imgHeight); } }) }

在上一步我们获取到了图片像素点信息,数据结果(data)如下:

data = [255, 255, 255, 255, 255, 61, 61, 255, 255, 0, 0, 255, 255,...]

data是一维数组,数组的前四个值[255, 255, 255, 255]为图片第一个像素点的rgba值(ctx.getImageData返回的透明度大小范围是从0 - 255的),[255, 61, 61, 255]是图片第二个像素点的rgba值,后面依次类推。如此便成功的将图片转化成了数据。

虽然图片成功转化成了数据,但这样的数据结构很难操作,我们期待能够将数据结构的表现形式与图片展示效果保持一致。

假如存在四个都是黑色的像素点(如下图),总宽高都为2,值为[0, 0, 0, 255,0, 0, 0, 255,0, 0, 0, 255,0, 0, 0, 255]。

在这里插入图片描述

通过某个函数转换,数据就变成了下列格式。

[ [[0, 0, 0, 255],[0, 0, 0, 255]], // 第一行 [[0, 0, 0, 255],[0, 0, 0, 255]] // 第二行 ]

上列数据格式和图片的展示结构保持了一致,可以很清晰的看出当前图形有多少行,每一行又有多少个像素点,以及每一个像素点的rgba值。

综合上面描述,可以编写函数normalize(代码如下)实现数据格式的转换。

/** * 将一维数组化为矩阵结构数组 * * @param { Array } data 一维数组 **/ function normalize(data) { const pixelPointArr = [] const result = [] const len = data.length / 4 // 将rgba数据形成一个数组 for (let i = 0; i const temp = [] for (let w = 0; w for (let h = 0; h // 排除透明度的比较 if (data[h][w].slice(0, 3).join() === originRGB.join()) { data[h][w] = [...endedRGB, data[h][w][3]]; } } } return data }

矩阵的数据操作完了,还需要调用restoreData函数将多维数组再转回一维数组传给putImageData方法进行图形渲染。

/** * 转化为一维数组 * * @param { Array } data 矩阵结构数组 **/ function restoreData(data) { const result = []; for (let h = 0; h result.push(...data[h][w]); } } return result; }

数据处理完毕后,将处理完的数据data传递给drawImage函数渲染成新图片(代码如下)。

渲染图像主要调用以下两个api。

ctx.createImageData(width, height) 创建新的空白ImageData对象,通过.data.set重新赋值。ctx.putImageData(imagedata, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight) 将图像数据(从指定的 ImageData 对象)放回画布上,可以只输入前三个参数。参数分别是: imagedata:包含像素值的数组对象。dx:源图像数据在目标画布中的位置偏移量(x 轴方向的偏移量)。dy:源图像数据在目标画布中的位置偏移量(y 轴方向的偏移量)。dirtyX:(可选)在源图像数据中,矩形区域左上角的位置。默认是整个图像数据的左上角(x 坐标)。dirtyY:(可选) 在源图像数据中,矩形区域左上角的位置。默认是整个图像数据的左上角(y 坐标)。dirtyWidth:(可选) 在源图像数据中,矩形区域的宽度。默认是图像数据的宽度。dirtyHeight:(可选) 在源图像数据中,矩形区域的高度。默认是图像数据的高度。 /** * 绘制图片 * * @param { Array } data 一维数组 **/ function drawImage(data) { const matrixObj = ctx.createImageData(imgWidth, imgHeight); matrixObj.data.set(data); ctx.putImageData(matrixObj, 0, 0); document.body.appendChild(canvas) }

这样整体流程就结束了,总体函数调用逻辑如下

getImageData('./images/xxx.png') .then((data) => { data = normalize(data); // 转化成多维数组 data = peeling(data, [183, 183, 183], [255, 255, 255]); // 换肤 data = restoreData(data); // 转化成一维数组 drawImage(data); // 绘制图像 })

至此新图片便成功渲染了出来。可以看下效果对比图。左图为原图,右图为处理之后的图片。将灰色背景(rgb值183, 183, 183)替换为白色背景(rgb值255, 255, 255)

在这里插入图片描述 在这里插入图片描述

回顾上述操作,编辑图像主要分解成以下四步。

将原始图片转化成矩阵数据(多维数组)依据需求操作矩阵将矩阵数据转换回一维数组渲染新图片

上述第二步操作是图像编辑的核心,很多复杂的变换效果可以通过编写矩阵算法实现。

为了加深理解,利用上述知识点实现一个图片旋转的需求。

旋转功能

假定存在最简单的情况如下图所示,其中左图存在四个像素点。第一行有两个像素点1和2(这里用序号代替rgba值)。

第二行也有两个像素点3和4。数据源转换成矩阵data后的值为 [[[1],[2]],[[3],[4]]]。

在这里插入图片描述

如何将左图按顺时针旋转90度变成右图

通过观察图中位置关系,只需要将data中的数据做位置变换,让data = [[[1],[2]],[[3],[4]]]变成data = [[[3],[1]],[[4],[2]]],就可以实现图片变换。

四个像素点可以直接用索引交换数组的值,但一张图片动辄几十万个像素,那该如何进行操作?

这种情况下通常需要编写一个基础算法来实现图片的旋转。

首先从下图中寻找规律,图中有左 - 中 - 右三种图片状态,为了从左图的1-2-3-4变成右图的3-1-4-2,可以通过以下两步实现.

在这里插入图片描述

寻找矩阵的高度的中心轴线,上下两侧按照轴线进行数据交换。比如左图1 - 2和3 - 4之间可以画一条轴线,上下两侧围绕轴线交换数据,第一行变成了3 - 4,第二行变成了1 - 2。通过第一步操作变成了中图的样子。

中图的对角线3 - 2和右图一致,剩下的将对角线两侧的数据对称交换就可以变成右图。比如将中图的1和4进行值交换。操作完后便实现了图片的旋转。值得注意的是4的数组索引是[0][1],而1的索引是[1][0],刚好索引顺序颠倒。

通过以上描述规律便可编写下面函数实现图片的旋转。下面的旋转算法只适用于正方形且长宽为偶数(长方形的图片要另外编写)。

/** * 图片旋转90度 * * @param { Array } data 矩阵结构数组 **/ function rotate90(data) { // 围绕中间行上下颠倒 const mid = imgHeight / 2; // 找出中间行 for (let h = 0; h const correspondingLine = imgHeight - 1 - h; [data[h][w], data[correspondingLine][w]] = [data[correspondingLine][w], data[h][w]] } } // 根据对角线进行值交换 for (let h = 0; h [data[h][w], data[w][h]] = [data[w][h], data[h][w]] } } return data }

实现效果如下,左图为原图,右图为旋转90后的图片

在这里插入图片描述 在这里插入图片描述

反相功能

实现思路是将图片画到canvas上,获取canvas的ImageData对象,对每个像素的颜色值进行反相处理。

/** * 对图片进行反相处理 * * @param { Array } data 一维数组 **/ function revertImg(data) { const pixelLen = data.length / 4 for (let i = 0; i


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有