JavaScript图像点处理|吸附实现|图片处理

在实际的业务中需要对图像做特殊处理,比如获取图像的像素点,获取图像的边缘信息,图像和图像之间需要做到吸附。有的小伙伴不知道如何操作,那么这篇文章会给你带来一定的收获。知道的小伙伴或者有更简单的方法,还请多多指教,虚心接受并学习。

1. 如何获取图像的像素点

JavaScript中获取图像的相关信息主要是靠Canvas来获取,借住api getImageData来实现。

假设我们有这个一张图片

测试
这是一张PNG-32 452 * 452的图片,现在通过getImageData拿到他上面的像素点信息

  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  // 假设我们的图片路径是
  const source = 'http://xxx.com/test.png';
  // 创建img对象
  const img = new Image();
  // 请求资源不需要凭证,如果服务器有做特殊限制,则这段代码无效
  img.crossOrigin = 'anonymous';
  img.onload = () => {
      canvas.width = img.width;
      canvas.height = img.height;
      // 将图片绘制到canvas中,绘制起始点为x: 0, y: 0
      ctx.drawImage(img, 0, 0);
      // 获取图片像素点信息,从x: 0, y: 0开始,到图片的宽度和高度
      const imgData = ctx.getImageData(0, 0, img.width, img.height);
      console.log(imgData);
  }
  img.src = source;

上面代码打印出图片像素点的数据

像素点信息
红色区域当前的像素点集合452 * 452 = 204304,总像素点有204304,那为什么是817216呢,像素点集合是每四个为一个点,这四个分别代表r(0~255),g(0~255), b(0~255), a(0~255), 那么应该在原有的基础上204304 * 4 = 817216这时的像素点就对了。

2. 处理像素点
  // imgData是第一步获取到的像素点信息
  const points = imgData.data;
  const len = points.length;
  // 定义每一行row
  let row = 0;
  // 定义存储所有坐标的集合
  const imgPoints = [];
  for (let i = 0; i < len; i += 4) {
      //每4个的第0个代表r
      const r = i;
      // 每4个的第1个代表g
      const g = i + 1;
      // 每4个的第2个代表b
      const b = i + 2;
      // 每4个的第3个代表a
      const a = i + 3;
      // 把像素点转换成坐标点
      /**
      * 这里判断是否为当前行的信息,用y来做判断
      * 图片宽度是452,如果是第一行的话y为1,第二行y为2...
      * 用当前递增i的值除以4在除以图片的宽度,向上取整,计算出第几行
      */
      const y = Math.ceil(i / 4 / imgData.width);
      // 如果y与当前row不相等,也就是从下一行开始
      if (y && y !== row) {
          // 将当前y赋值给row;
          row = y;
          // 将当前行设置成集合
          imgPoints[row] = [];
      }
      // 如果当前集合存在
      if (imgPoints[row]) {
          // 如果透明度a的值存在
          if (a) {
              // 算出当前这一行上每一个像素点的位置
              const x = (i / 4) - (imgData.width * (y - 1));
              // 将x, y,当前这一行,数据结构为map
              imgPoints[row].push(
                  {
                      x,
                      y
                  }
              );
          } else {
              // 如果当前的透明值a不存在则存放0
              imgPoints[row].push(0);
          }
      }
  }

这样我们的一个数据结构已经成功了

 // 最终的数据结果,每一行都是一个集合,每一行集合里面有数据的则是map结构,没有则是0
  [
    // 第一行的坐标, y为1
     [
         {
             x: 1,
             y: 1
         },
         {
             x: 2,
             y: 1,
         },
         // 当前没有坐标点,是透明点,记录0
         0
         // ......
      ],
      // 第二行的坐标, y为2
      [
         {
             x: 1,
             y: 2
          },
          {
              x: 2,
              y: 2
          },
          // 当前没有坐标点,是透明点,记录0
          0
          // ......
      ]
  ]
2. 获取图像边缘
  // 假设我们的数据结构
  [0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0]
  // 0代表没有坐标点,1代表我们的map集合 
  // 现在我们需要将其实和结尾连续的0清除
  // 得到的结果
  [1, 1, 1, 0, 0, 1, 1, 1]
  // 现在继续将数据结构进一步优化,将第一个1, 到第一个0的位置但不包含0取出
  [1, 1, 0, 0, 1, 1, 1]
  // 继续将后面的数据结构一同取出
  [1, 1, 0, 0, 1, 1]
  // 最终将0过滤掉
  [1, 1, 1, 1]
  // 上面是我们需要的数据结构
 

  // 由此得出判断判断条件
  const f = 数组的第0个 === 1;
  const l = 数组的最后一个 === 1;
  const conditionLeft = 当前值 !== 0 && 当前值的前一个值 === 0;
  const conditionRight = 当前值 !== 0 && 当前值的后一个值 === 0;
  // 这个判断则过滤当前这一行所有的数据,并不是y,而是x
  
  // 接下来过滤y
  // 假设数据结构
  [
      [0, 0, 1, 1, 0, 0],
      [1 ,1, 0, 1, 1, 0]
  ]
  // 此时我们应该将所有列,注意:不是行,行是x,现在我们要过滤列。
  // 判断条件和行的判断条件基本一致
  const f = 第1行 === 1;
  const l = 最后1行 === 1;
  const conditionTop = 当前行的当前值的上一行对应的值 === 0 && 当前行的当前值 !== 0;
  const conditionBottom = 当前行的当前值的下一个对应的值 === 0 && 当前行的当前值 !== 0;
  // 当前判断条件将过滤所有的y

接下来我们需要将过滤x和过滤y的条件,筛选出来的数据存放在不同的数据结构中

  // 定义存放x的坐标点
  const xPoints = [];
  // 定义存放y的坐标点
  const yPoints = [];
  // imgPoints是存放所有的数据结构,是个多维数组(二维数组)
  imgPoints.forEach((points, i) => {
      points.forEach((row, j) => {
          // 首先套用x的判断
          if (
              (j === 0 && row !== 0) ||
              (j === points.length - 1 && row !== 0) ||
              (row !== 0 && typeof points[j - 1] !== 'undefined' && points[j - 1] === 0) ||
              (row !== 0 && typeof points[j + 1] !== 'undefined' && points[j + 1] === 0)
          ) {
              xPoints.push(row);
          }
          
          if (
              (i === 0 && row !== 0) ||
              (i === imgPoints[imgPoints.length - 1] && k !== 0) ||
              (typeof imgPoints[i - 1] !== 'undefined' && imgPoints[i - 1][j] === 0 && row !== 0) ||
              (typeof imgPoints[i + 1] !== 'undefined' && imgPoints[i + 1][j] === 0 && row !== 0)
          ) {
              yPoints.push(row);
          }
      })
  })
  // 上面的代码就拿到了图形边缘的所有坐标点,不管这个图形有多复杂,还是这个图形分开的多个小图形

假设我们上述不知道,并且我们的程序中不知道这个图形的颜色。推荐大家使用第三方的库,可以转换成边缘图像。
本人推荐https://github.com/miguelmota/sobel,这个库的代码很少,有兴趣可以阅读一下他里面的核心算法。我还使用了一些其他的图像转边缘图像的其他库,但出来的效果并不是很好。

3. 图像吸附

当你拿到边缘点的时候,你就可以对你的图像进行吸附的功能操作。吸附功能还要根据各自的业务去实现,在这里不在coding。

总结

写到这里,有的人会说到,为什么不用一元二次方程,或者点斜式方程去计算斜边的点
其实也是可以的,但这种情况仅限于固定程序。已知图形的外边缘的点。比如一个三角形,已知3个点,那么这个之后你可以通过方程式求出斜边的坐标点。但仅限于已知程序,如果一张图像是通过程序导入,或者外部资源引入的方式,那方程式计算出来要比直接取点要来的麻烦,麻烦的因素是多边形和不规则图形,以及分散图形。套用方程式会有额外的工作。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352