JavaScript圖像點(diǎn)處理|吸附實(shí)現(xiàn)|圖片處理

在實(shí)際的業(yè)務(wù)中需要對(duì)圖像做特殊處理柑土,比如獲取圖像的像素點(diǎn),獲取圖像的邊緣信息绊汹,圖像和圖像之間需要做到吸附稽屏。有的小伙伴不知道如何操作,那么這篇文章會(huì)給你帶來一定的收獲西乖。知道的小伙伴或者有更簡(jiǎn)單的方法狐榔,還請(qǐng)多多指教,虛心接受并學(xué)習(xí)获雕。

1. 如何獲取圖像的像素點(diǎn)

JavaScript中獲取圖像的相關(guān)信息主要是靠Canvas來獲取薄腻,借住api getImageData來實(shí)現(xiàn)。

假設(shè)我們有這個(gè)一張圖片

測(cè)試
這是一張PNG-32 452 * 452的圖片届案,現(xiàn)在通過getImageData拿到他上面的像素點(diǎn)信息

  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  // 假設(shè)我們的圖片路徑是
  const source = 'http://xxx.com/test.png';
  // 創(chuàng)建img對(duì)象
  const img = new Image();
  // 請(qǐng)求資源不需要憑證庵楷,如果服務(wù)器有做特殊限制,則這段代碼無效
  img.crossOrigin = 'anonymous';
  img.onload = () => {
      canvas.width = img.width;
      canvas.height = img.height;
      // 將圖片繪制到canvas中楣颠,繪制起始點(diǎn)為x: 0, y: 0
      ctx.drawImage(img, 0, 0);
      // 獲取圖片像素點(diǎn)信息尽纽,從x: 0, y: 0開始,到圖片的寬度和高度
      const imgData = ctx.getImageData(0, 0, img.width, img.height);
      console.log(imgData);
  }
  img.src = source;

上面代碼打印出圖片像素點(diǎn)的數(shù)據(jù)

像素點(diǎn)信息
紅色區(qū)域當(dāng)前的像素點(diǎn)集合452 * 452 = 204304球碉,總像素點(diǎn)有204304蜓斧,那為什么是817216呢,像素點(diǎn)集合是每四個(gè)為一個(gè)點(diǎn)睁冬,這四個(gè)分別代表r(0~255)挎春,g(0~255), b(0~255), a(0~255), 那么應(yīng)該在原有的基礎(chǔ)上204304 * 4 = 817216這時(shí)的像素點(diǎn)就對(duì)了看疙。

2. 處理像素點(diǎn)
  // imgData是第一步獲取到的像素點(diǎn)信息
  const points = imgData.data;
  const len = points.length;
  // 定義每一行row
  let row = 0;
  // 定義存儲(chǔ)所有坐標(biāo)的集合
  const imgPoints = [];
  for (let i = 0; i < len; i += 4) {
      //每4個(gè)的第0個(gè)代表r
      const r = i;
      // 每4個(gè)的第1個(gè)代表g
      const g = i + 1;
      // 每4個(gè)的第2個(gè)代表b
      const b = i + 2;
      // 每4個(gè)的第3個(gè)代表a
      const a = i + 3;
      // 把像素點(diǎn)轉(zhuǎn)換成坐標(biāo)點(diǎn)
      /**
      * 這里判斷是否為當(dāng)前行的信息,用y來做判斷
      * 圖片寬度是452直奋,如果是第一行的話y為1能庆,第二行y為2...
      * 用當(dāng)前遞增i的值除以4在除以圖片的寬度,向上取整脚线,計(jì)算出第幾行
      */
      const y = Math.ceil(i / 4 / imgData.width);
      // 如果y與當(dāng)前row不相等搁胆,也就是從下一行開始
      if (y && y !== row) {
          // 將當(dāng)前y賦值給row;
          row = y;
          // 將當(dāng)前行設(shè)置成集合
          imgPoints[row] = [];
      }
      // 如果當(dāng)前集合存在
      if (imgPoints[row]) {
          // 如果透明度a的值存在
          if (a) {
              // 算出當(dāng)前這一行上每一個(gè)像素點(diǎn)的位置
              const x = (i / 4) - (imgData.width * (y - 1));
              // 將x, y,當(dāng)前這一行,數(shù)據(jù)結(jié)構(gòu)為map
              imgPoints[row].push(
                  {
                      x,
                      y
                  }
              );
          } else {
              // 如果當(dāng)前的透明值a不存在則存放0
              imgPoints[row].push(0);
          }
      }
  }

這樣我們的一個(gè)數(shù)據(jù)結(jié)構(gòu)已經(jīng)成功了

 // 最終的數(shù)據(jù)結(jié)果邮绿,每一行都是一個(gè)集合渠旁,每一行集合里面有數(shù)據(jù)的則是map結(jié)構(gòu),沒有則是0
  [
    // 第一行的坐標(biāo)船逮, y為1
     [
         {
             x: 1,
             y: 1
         },
         {
             x: 2,
             y: 1,
         },
         // 當(dāng)前沒有坐標(biāo)點(diǎn)顾腊,是透明點(diǎn),記錄0
         0
         // ......
      ],
      // 第二行的坐標(biāo)挖胃, y為2
      [
         {
             x: 1,
             y: 2
          },
          {
              x: 2,
              y: 2
          },
          // 當(dāng)前沒有坐標(biāo)點(diǎn)杂靶,是透明點(diǎn),記錄0
          0
          // ......
      ]
  ]
2. 獲取圖像邊緣
  // 假設(shè)我們的數(shù)據(jù)結(jié)構(gòu)
  [0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0]
  // 0代表沒有坐標(biāo)點(diǎn)酱鸭,1代表我們的map集合 
  // 現(xiàn)在我們需要將其實(shí)和結(jié)尾連續(xù)的0清除
  // 得到的結(jié)果
  [1, 1, 1, 0, 0, 1, 1, 1]
  // 現(xiàn)在繼續(xù)將數(shù)據(jù)結(jié)構(gòu)進(jìn)一步優(yōu)化吗垮,將第一個(gè)1, 到第一個(gè)0的位置但不包含0取出
  [1, 1, 0, 0, 1, 1, 1]
  // 繼續(xù)將后面的數(shù)據(jù)結(jié)構(gòu)一同取出
  [1, 1, 0, 0, 1, 1]
  // 最終將0過濾掉
  [1, 1, 1, 1]
  // 上面是我們需要的數(shù)據(jù)結(jié)構(gòu)
 

  // 由此得出判斷判斷條件
  const f = 數(shù)組的第0個(gè) === 1;
  const l = 數(shù)組的最后一個(gè) === 1;
  const conditionLeft = 當(dāng)前值 !== 0 && 當(dāng)前值的前一個(gè)值 === 0;
  const conditionRight = 當(dāng)前值 !== 0 && 當(dāng)前值的后一個(gè)值 === 0;
  // 這個(gè)判斷則過濾當(dāng)前這一行所有的數(shù)據(jù)凹髓,并不是y烁登,而是x
  
  // 接下來過濾y
  // 假設(shè)數(shù)據(jù)結(jié)構(gòu)
  [
      [0, 0, 1, 1, 0, 0],
      [1 ,1, 0, 1, 1, 0]
  ]
  // 此時(shí)我們應(yīng)該將所有列,注意:不是行扁誓,行是x防泵,現(xiàn)在我們要過濾列。
  // 判斷條件和行的判斷條件基本一致
  const f = 第1行 === 1;
  const l = 最后1行 === 1;
  const conditionTop = 當(dāng)前行的當(dāng)前值的上一行對(duì)應(yīng)的值 === 0 && 當(dāng)前行的當(dāng)前值 !== 0;
  const conditionBottom = 當(dāng)前行的當(dāng)前值的下一個(gè)對(duì)應(yīng)的值 === 0 && 當(dāng)前行的當(dāng)前值 !== 0;
  // 當(dāng)前判斷條件將過濾所有的y

接下來我們需要將過濾x和過濾y的條件蝗敢,篩選出來的數(shù)據(jù)存放在不同的數(shù)據(jù)結(jié)構(gòu)中

  // 定義存放x的坐標(biāo)點(diǎn)
  const xPoints = [];
  // 定義存放y的坐標(biāo)點(diǎn)
  const yPoints = [];
  // imgPoints是存放所有的數(shù)據(jù)結(jié)構(gòu)捷泞,是個(gè)多維數(shù)組(二維數(shù)組)
  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);
          }
      })
  })
  // 上面的代碼就拿到了圖形邊緣的所有坐標(biāo)點(diǎn),不管這個(gè)圖形有多復(fù)雜寿谴,還是這個(gè)圖形分開的多個(gè)小圖形

假設(shè)我們上述不知道锁右,并且我們的程序中不知道這個(gè)圖形的顏色。推薦大家使用第三方的庫讶泰,可以轉(zhuǎn)換成邊緣圖像咏瑟。
本人推薦https://github.com/miguelmota/sobel,這個(gè)庫的代碼很少痪署,有興趣可以閱讀一下他里面的核心算法码泞。我還使用了一些其他的圖像轉(zhuǎn)邊緣圖像的其他庫,但出來的效果并不是很好狼犯。

3. 圖像吸附

當(dāng)你拿到邊緣點(diǎn)的時(shí)候余寥,你就可以對(duì)你的圖像進(jìn)行吸附的功能操作领铐。吸附功能還要根據(jù)各自的業(yè)務(wù)去實(shí)現(xiàn),在這里不在coding宋舷。

總結(jié)

寫到這里绪撵,有的人會(huì)說到,為什么不用一元二次方程祝蝠,或者點(diǎn)斜式方程去計(jì)算斜邊的點(diǎn)音诈。
其實(shí)也是可以的,但這種情況僅限于固定程序绎狭。已知圖形的外邊緣的點(diǎn)细溅。比如一個(gè)三角形,已知3個(gè)點(diǎn)坟岔,那么這個(gè)之后你可以通過方程式求出斜邊的坐標(biāo)點(diǎn)谒兄。但僅限于已知程序,如果一張圖像是通過程序?qū)肷绺叮蛘咄獠抠Y源引入的方式,那方程式計(jì)算出來要比直接取點(diǎn)要來的麻煩邻耕,麻煩的因素是多邊形和不規(guī)則圖形鸥咖,以及分散圖形。套用方程式會(huì)有額外的工作兄世。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末啼辣,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子御滩,更是在濱河造成了極大的恐慌鸥拧,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件削解,死亡現(xiàn)場(chǎng)離奇詭異富弦,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)氛驮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門腕柜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人矫废,你說我怎么就攤上這事盏缤。” “怎么了蓖扑?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵唉铜,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我律杠,道長(zhǎng)潭流,這世上最難降的妖魔是什么柿赊? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮幻枉,結(jié)果婚禮上碰声,老公的妹妹穿的比我還像新娘。我一直安慰自己熬甫,他們只是感情好胰挑,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著椿肩,像睡著了一般瞻颂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上郑象,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天贡这,我揣著相機(jī)與錄音,去河邊找鬼厂榛。 笑死盖矫,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的击奶。 我是一名探鬼主播辈双,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼柜砾!你這毒婦竟也來了湃望?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤痰驱,失蹤者是張志新(化名)和其女友劉穎证芭,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體担映,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡废士,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了另萤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片湃密。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖四敞,靈堂內(nèi)的尸體忽然破棺而出泛源,到底是詐尸還是另有隱情,我是刑警寧澤忿危,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布达箍,位于F島的核電站,受9級(jí)特大地震影響铺厨,放射性物質(zhì)發(fā)生泄漏缎玫。R本人自食惡果不足惜硬纤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望赃磨。 院中可真熱鬧筝家,春花似錦、人聲如沸邻辉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽值骇。三九已至莹菱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間吱瘩,已是汗流浹背道伟。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留使碾,地道東北人蜜徽。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像部逮,于是被迫代替她去往敵國(guó)和親娜汁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353