图片处理不用愁,给你十个小帮手

报错记录 | 'Access-Control-Allow-Origin' 跨域读取阻止 报错信息 禁用 https://stackoverflow.com/questions/3102819/disable-same-origin-policy-in-chrome 阅读详情

本文阿宝哥会为小伙伴们隆重介绍用于图片处理的十个 “小帮手”,他们各个身怀绝技,拥有模糊、压缩、裁剪、旋转、合成、比对等技能。相信认识他们之后,你将能够轻松应对大多数的图片处理场景。

不过在介绍 “小帮手” 前,阿宝哥会先介绍一些图片相关的基础知识。此外,为了让小伙伴们能够学习更多图片相关的知识,阿宝哥精心准备了 “阿宝哥有话说” 章节。该章节你将会学到以下知识:

  • 如何区分图片的类型(非文件后缀名);
  • 如何获取图片的尺寸(非右键查看图片信息);
  • 如何预览本地图片(非图片阅读器);
  • 如何实现图片压缩(非图片压缩工具);
  • 如何操作位图像素数据(非 PS 等图片处理软件);
  • 如何实现图片隐写(非肉眼可见)。

十个图片处理 “小帮手” 已经已经迫不及待想与你见面,还在犹豫什么?赶紧出发吧!

一、基础知识

1.1 位图

位图图像(bitmap),亦称为点阵图像或栅格图像,是由称作像素(图片元素)的单个点组成的。 这些点可以进行不同的排列和染色以构成图样。当放大位图时,可以看见赖以构成整个图像的无数单个方块。扩大位图尺寸的效果是增大单个像素,从而使线条和形状显得参差不齐。

用数码相机拍摄的照片、扫描仪扫描的图片以及计算机截屏图等都属于位图。 位图的特点是可以表现色彩的变化和颜色的细微过渡,产生逼真的效果,缺点是在保存时需要记录每一个像素的位置和颜色值,占用较大的存储空间。常用的位图处理软件有 Photoshop、Painter 和 Windows 系统自带的画图工具等。

分辨率是位图不可逾越的壁垒,在对位图进行缩放、旋转等操作时,无法生产新的像素,因此会放大原有的像素填补空白,这样会让图片显得不清晰。

(图片来源:https://zh.wikipedia.org/wiki/%E4%BD%8D%E5%9B%BE)

图中的小方块被称为像素,这些小方块都有一个明确的位置和被分配的色彩数值,小方格颜色和位置就决定该图像所呈现出来的样子。

可以将像素视为整个图像中不可分割的单位或者是元素。不可分割的意思是它不能够再切割成更小单位抑或是元素,它是以一个单一颜色的小格存在。 每一个点阵图像包含了一定量的像素,这些像素决定图像在屏幕上所呈现的大小。

1.2 矢量图

所谓矢量图,就是使用直线和曲线来描述的图形,构成这些图形的元素是一些点、线、矩形、多边形、圆和弧线等,它们都是通过数学公式计算获得的,具有编辑后不失真的特点。 例如一幅画的矢量图形实际上是由线段形成外框轮廓,由外框的颜色以及外框所封闭的颜色决定画显示出的颜色。

矢量图以几何图形居多,图形可以无限放大,不变色、不模糊。 常用于图案、标志、VI、文字等设计。常用软件有:CorelDraw、Illustrator、Freehand、XARA、CAD 等。

这里我们以 Web 开发者比较熟悉的 SVG(Scalable Vector Graphics —— 可缩放矢量图形)为例,来了解一下 SVG 的结构:

可缩放矢量图形(英语:Scalable Vector Graphics,SVG)是一种基于可扩展标记语言(XML),用于描述二维矢量图形的图形格式。SVG 由 W3C 制定,是一个开放标准。

SVG 主要支持以下几种显示对象:

  • 矢量显示对象,基本矢量显示对象包括矩形、圆、椭圆、多边形、直线、任意曲线等;
  • 嵌入式外部图像,包括 PNG、JPEG、SVG 等;
  • 文字对象。

了解完位图与矢量图的区别,下面我们来介绍一下位图的数学表示。

1.3 位图的数学表示

位图的像素都分配有特定的位置和颜色值。每个像素的颜色信息由 RGB 组合或者灰度值表示。

根据位深度,可将位图分为1、4、8、16、24 及 32 位图像等。每个像素使用的信息位数越多,可用的颜色就越多,颜色表现就越逼真,相应的数据量越大。

1.3.1 二值图像

位深度为 1 的像素位图只有两个可能的值(黑色和白色),所以又称为二值图像。二值图像的像素点只有黑白两种情况,因此每个像素点可以由 0 和 1 来表示。

比如一张 4 * 4 二值图像:

1 1 0 1
1 1 0 1
1 0 0 0
1 0 1 0

1.3.2 RGB 图像

RGB 图像由三个颜色通道组成,其中 RGB 代表红、绿、蓝三个通道的颜色。8 位/通道的 RGB 图像中的每个通道有 256 个可能的值,这意味着该图像有 1600 万个以上可能的颜色值。有时将带有 8 位/通道(bpc)的 RGB 图像称作 24 位图像(8 位 x 3 通道 = 24 位数据/像素)。通常将使用 24 位 RGB 组合数据位表示的的位图称为真彩色位图。

RGB 彩色图像可由三种矩阵表示:一种代表像素中红色的强度,一种代表绿色,另一种代表蓝色。

(图片来源:https://freecontent.manning.com/the-computer-vision-pipeline-part-2-input-images/)

图像处理的本质实际上就是对这些像素矩阵进行计算。 其实位图中的图像类型,除了二值图像和 RGB 图像之外,还有灰度图像、索引图像和 YUV 图像。这里我们不做过多介绍,感兴趣的小伙伴,可以自行查阅相关资料。

二、图片处理库

2.1 AlloyImage

基于 HTML 5 的专业级图像处理开源引擎。

https://github.com/AlloyTeam/AlloyImage

AlloyImage 基于 HTML5 技术的专业图像处理库,来自腾讯 AlloyTeam 团队。它拥有以下功能特性:

  • 基于多图层操作 —— 一个图层的处理不影响其他图层;
  • 与 PS 对应的 17 种图层混合模式 —— 便于 PS 处理教程的无缝迁移;
  • 多种基本滤镜处理效果 —— 基本滤镜不断丰富、可扩展;
  • 基本的图像调节功能 —— 色相、饱和度、对比度、亮度、曲线等;
  • 简单快捷的 API —— 链式处理、API 简洁易用、传参灵活;
  • 多种组合效果封装 —— 一句代码轻松实现一种风格;
  • 接口一致的单、多线程支持 —— 单、多线程切换无需更改一行代码,多线程保持快捷 API 特性。

对于该库 AlloyTeam 团队建议的使用场景如下:

  • 桌面软件客户端内嵌网页运行方式 >>> 打包 Webkit 内核:用户较大头像上传风格处理、用户相册风格处理(处理时间平均 < 1s);
  • Win8 Metro 应用 >>> 用户上传头像,比较小的图片风格处理后上传(Win8 下 IE 10 支持多线程);
  • Mobile APP >>> Andriod 平台、iOS 平台小图风格 Web 处理的需求,如 PhoneGap 应用,在线头像上传时的风格处理、Mobile Web 端分享图片时风格处理等。

使用示例

// $AI或AlloyImage初始化一个AlloyImage对象
var ps = $AI(img, 600).save('jpg', 0.6);

// save将合成图片保存成base64格式字符串
var string = AlloyImage(img).save('jpg', 0.8);

// saveFile将合成图片下载到本地
img.onclick = function(){
  AlloyImage(this).saveFile('处理后图像.jpg', 0.8);
}

在线示例

http://alloyteam.github.io/AlloyImage/

(图片来源:http://alloyteam.github.io/AlloyImage/)

2.2 blurify

blurify.js is a tiny(~2kb) library to blurred pictures, support graceful downgrade from css mode to canvas mode.

https://github.com/JustClear/blurify

blurify.js 是一个用于图片模糊,很小的 JavaScript 库(约 2 kb),并支持从 CSS 模式到 Canvas 模式的优雅降级。该插件支持三种模式:

  • css 模式:使用 filter 属性,默认模式;
  • canvas 模式:使用 canvas 导出 base64;
  • auto 模式:优先使用 css 模式,否则自动切换到 canvas 模式。

使用示例

import blurify from 'blurify';

new blurify({
    images: document.querySelectorAll('.blurify'),
    blur: 6,
    mode: 'css',
});

// or in shorthand

blurify(6, document.querySelectorAll('.blurify'));

在线示例

https://justclear.github.io/blurify/

(图片来源:https://justclear.github.io/blurify/)

看到这里是不是有些小伙伴觉得只是模糊处理而已,觉得不过瘾,能不能来点更酷的。嘿嘿,有求必应!阿宝哥立马来个 “酷炫叼” 的库 —— midori,该库用于为背景图创建动画,使用 three.js 编写并使用 WebGL。本来是想给个演示动图,无奈单个 Gif 太大,只能放个体验地址,感兴趣的小伙伴自行体验一下。

midori 示例地址:https://aeroheim.github.io/midori/

2.3 cropperjs

JavaScript image cropper.

https://github.com/fengyuanchen/cropperjs

Cropper.js 是一款非常强大却又简单的图片裁剪工具,它可以进行非常灵活的配置,支持手机端使用,支持包括 IE9 以上的现代浏览器。它可以用于满足诸如裁剪头像上传、商品图片编辑之类的需求。

Cropper.js 支持以下特性:

  • 支持 39 个配置选项;
  • 支持 27 个方法;
  • 支持 6 种事件;
  • 支持 touch(移动端);
  • 支持缩放、旋转和翻转;
  • 支持在画布上裁剪;
  • 支持在浏览器端通过画布裁剪图像;
  • 支持处理 Exif 方向信息;
  • 跨浏览器支持。

可交换图像文件格式(英语:Exchangeable image file format,官方简称 Exif),是专门为数码相机的照片设定的文件格式,可以记录数码照片的属性信息和拍摄数据。Exif 可以附加于 JPEG、TIFF、RIFF 等文件之中,为其增加有关数码相机拍摄信息的内容和索引图或图像处理软件的版本信息。

Exif 信息以 0xFFE1 作为开头标记,后两个字节表示 Exif 信息的长度。所以 Exif 信息最大为 64 kB,而内部采用 TIFF 格式。

使用示例

// import 'cropperjs/dist/cropper.css';
import Cropper from 'cropperjs';

const image = document.getElementById('image');
const cropper = new Cropper(image, {
  aspectRatio: 16 / 9,
  crop(event) {
    console.log(event.detail.x);
    console.log(event.detail.y);
    console.log(event.detail.width);
    console.log(event.detail.height);
    console.log(event.detail.rotate);
    console.log(event.detail.scaleX);
    console.log(event.detail.scaleY);
  },
});

在线示例

https://fengyuanchen.github.io/cropperjs/

2.4 compressorjs

JavaScript image compressor.

https://github.com/fengyuanchen/compressorjs

compressorjs 是 JavaScript 图像压缩器。使用浏览器原生的 canvas.toBlob API 进行压缩工作,这意味着它是有损压缩。通常的使用场景是,在浏览器端图片上传之前对其进行预压缩。

在浏览器端要实现图片压缩,除了使用 canvas.toBlob API 之外,还可以使用 Canvas 提供的另一个 API,即 toDataURL API,它接收 type 和 encoderOptions 两个可选参数。

其中 type 表示图片格式,默认为 image/png。而 encoderOptions 用于表示图片的质量,在指定图片格式为 image/jpeg 或 image/webp 的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92,其他参数会被忽略。

相比 canvas.toDataURL API 来说,canvas.toBlob API 是异步的,因此多了个 callback 参数,这个 callback 回调方法默认的第一个参数就是转换好的 blob 文件信息。canvas.toBlob 的签名如下:

canvas.toBlob(callback, mimeType, qualityArgument)

使用示例

import axios from 'axios';
import Compressor from 'compressorjs';

// <input type="file" id="file" accept="image/*">
document.getElementById('file').addEventListener('change', (e) => {
  const file = e.target.files[0];

  if (!file) {
    return;
  }
  new Compressor(file, {
    quality: 0.6,
    success(result) {
      const formData = new FormData();
      // The third parameter is required for server
      formData.append('file', result, result.name);

      // Send the compressed image file to server with XMLHttpRequest.
      axios.post('/path/to/upload', formData).then(() => {
        console.log('Upload success');
      });
    },
    error(err) {
      console.log(err.message);
    },
  });
});

在线示例

https://fengyuanchen.github.io/compressorjs/

2.5 fabric.js

Javascript Canvas Library, SVG-to-Canvas (& canvas-to-SVG) Parser.

https://github.com/fabricjs/fabric.js

Fabric.js 是一个框架,可让你轻松使用 HTML5 Canvas 元素。它是一个位于 Canvas 元素之上的交互式对象模型,同时也是一个 SVG-to-canvas 的解析器。

使用 Fabric.js,你可以在画布上创建和填充对象。所谓的对象,可以是简单的几何形状,比如矩形,圆形,椭圆形,多边形,或更复杂的形状,包含数百或数千个简单路径。然后,你可以使用鼠标缩放,移动和旋转这些对象。并修改它们的属性 —— 颜色,透明度,z-index 等。此外你还可以一起操纵这些对象,即通过简单的鼠标选择将它们分组。

Fabric.js 支持所有主流的浏览器,具体的兼容情况如下:

  • Firefox 2+
  • Safari 3+
  • Opera 9.64+
  • Chrome(所有版本)
  • IE10,IE11,Edge

使用示例

<!DOCTYPE html>
<html>
<head></head>
<body>
    <canvas id="canvas" width="300" height="300"></canvas>
    <script src="lib/fabric.js"></script>
    <script>
        var canvas = new fabric.Canvas('canvas');
        var rect = new fabric.Rect({
            top : 100,
            left : 100,
            width : 60,
            height : 70,
            fill : 'red'
        });

        canvas.add(rect);
    </script>
</body>
</html>

在线示例

http://fabricjs.com/kitchensink

(图片来源:https://github.com/fabricjs/fabric.js)

2.6 Resemble.js

Image analysis and comparison

https://github.com/rsmbl/Resemble.js

Resemble.js 使用 HTML Canvas 和 JavaScript 来实现图片的分析和比较。兼容大于 8.0 的 Node.js 版本。

使用示例

// 比较两张图片
var diff = resemble(file)
    .compareTo(file2)
    .ignoreColors()
    .onComplete(function(data) {
        console.log(data);
     /*
	    {
	       misMatchPercentage : 100, // %
	       isSameDimensions: true, // or false
	       dimensionDifference: { width: 0, height: -1 }, 
	       getImageDataUrl: function(){}
	    }
	   */
});

在线示例

http://rsmbl.github.io/Resemble.js/

2.7 Pica

Resize image in browser with high quality and high speed

https://github.com/nodeca/pica

Pica 可用于在浏览器中调整图像大小,没有像素化并且相当快。它会自动选择最佳的可用技术:webworkers,webassembly,createImageBitmap,纯 JS。

借助 Pica,你可以实现以下功能:

  • 减小大图像的上传大小,节省上传时间;
  • 在图像处理上节省服务器资源;
  • 在浏览器中生成缩略图。

使用示例

const pica = require('pica')();

// 调整画布/图片的大小
pica.resize(from, to, {
  unsharpAmount: 80,
  unsharpRadius: 0.6,
  unsharpThreshold: 2
})
.then(result => console.log('resize done!'));

// 调整大小并转换为Blob
pica.resize(from, to)
  .then(result => pica.toBlob(result, 'image/jpeg', 0.90))
  .then(blob => console.log('resized to canvas & created blob!'));

在线示例

http://nodeca.github.io/pica/demo/

2.8 tui.image-editor

🍞🎨 Full-featured photo image editor using canvas. It is really easy, and it comes with great filters.

https://github.com/nhn/tui.image-editor

tui.image-editor 是使用 HTML5 Canvas 的全功能图像编辑器。它易于使用,并提供强大的过滤器。同时它支持对图像进行裁剪、翻转、旋转、绘图、形状、文本、遮罩和图片过滤等操作。

tui.image-editor 的浏览器兼容情况如下:

  • Chrome
  • Edge
  • Safari
  • Firefox
  • IE 10+

使用示例

// Image editor
var imageEditor = new tui.ImageEditor("#tui-image-editor-container", {
     includeUI: {
       loadImage: {
         path: "img/sampleImage2.png",
         name: "SampleImage",
       },
       theme: blackTheme, // or whiteTheme
         initMenu: "filter",
         menuBarPosition: "bottom",
       },
       cssMaxWidth: 700,
       cssMaxHeight: 500,
       usageStatistics: false,
});

window.onresize = function () {
  imageEditor.ui.resizeEditor();
};

在线示例

https://ui.toast.com/tui-image-editor/

2.9 gif.js

JavaScript GIF encoding library

https://github.com/jnordberg/gif.js

gif.js 是运行在浏览器端的 JavaScript GIF 编码器。它使用类型化数组和 Web Worker 在后台渲染每一帧,速度真的很快。该库可工作在支持:Web Workers,File API 和 Typed Arrays 的浏览器中。

gif.js 的浏览器兼容情况如下:

  • Google Chrome
  • Firefox 17
  • Safari 6
  • Internet Explorer 10
  • Mobile Safari iOS 6

使用示例

var gif = new GIF({
  workers: 2,
  quality: 10
});

// add an image element
gif.addFrame(imageElement);

// or a canvas element
gif.addFrame(canvasElement, {delay: 200});

// or copy the pixels from a canvas context
gif.addFrame(ctx, {copy: true});

gif.on('finished', function(blob) {
  window.open(URL.createObjectURL(blob));
});

gif.render();

在线示例

http://jnordberg.github.io/gif.js/

2.10 Sharp

High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images. Uses the libvips library.

https://github.com/lovell/sharp

Sharp 的典型应用场景是将常见格式的大图像转换为尺寸较小,对网络友好的 JPEG,PNG 和 WebP 格式的图像。由于其内部使用 libvips ,使得调整图像大小通常比使用 ImageMagick 和 GraphicsMagick 设置快 4-5 倍 。除了支持调整图像大小之外,Sharp 还支持旋转、提取、合成和伽马校正等功能。

Sharp 支持读取 JPEG,PNG,WebP,TIFF,GIF 和 SVG 图像。输出图像可以是 JPEG,PNG,WebP 和 TIFF 格式,也可以是未压缩的原始像素数据。

使用示例

// 改变图像尺寸
sharp(inputBuffer)
  .resize(320, 240)
  .toFile('output.webp', (err, info) => { ... });
       
// 旋转输入图像并改变图片尺寸                                         
sharp('input.jpg')
  .rotate()
  .resize(200)
  .toBuffer()
  .then( data => { ... })
  .catch( err => { ... });                                         

在线示例

https://segmentfault.com/a/1190000012903787

该示例是来自阿宝哥 18 年写的 “Sharp 牛刀小试之生成专属分享图片” 这篇文章,主要是利用 Sharp 提供的图片合成功能为每个用户生成专属的分享海报,感兴趣的小伙伴可以阅读一下原文哟。

const sharp = require("sharp");
const TextToSVG = require("text-to-svg");
const path = require("path");

// 加载字体文件
const textToSVG = TextToSVG.loadSync(path.join(__dirname, "./simhei.ttf"));

// 创建圆形SVG,用于实现头像裁剪
const roundedCorners = new Buffer(
  '<svg><circle r="90" cx="90" cy="90"/></svg>'
);

// 设置SVG文本元素相关参数
const attributes = { fill: "white" };
const svgOptions = {
  x: 0,
  y: 0,
  fontSize: 32,
  anchor: "top",
  attributes: attributes
};

/**
 * 使用文本生成SVG
 * @param {*} text 
 * @param {*} options 
 */
function textToSVGFn(text, options = svgOptions) {
  return textToSVG.getSVG(text, options);
}

/**
 * 图层叠加生成分享图片
 * @param {*} options 
 * 
 */
async function genShareImage(options) {
  const { backgroudPath, avatarPath, qrcodePath, 
    userName, words, likes, outFilePath
  } = options;

  // 背景图片
  const backgroudBuffer = sharp(path.join(__dirname, backgroudPath)).toBuffer({
    resolveWithObject: true
  });

  const backgroundImageInfo = await backgroudBuffer;
  // 头像图片
  const avatarBuffer = await genCircleAvatar(path.join(__dirname, avatarPath));

  // 二维码图片
  const qrCodeBuffer = await sharp(path.join(__dirname, qrcodePath))
    .resize(180)
    .toBuffer({
      resolveWithObject: true
    });

  // 用户名
  const userNameSVG = textToSVGFn(userName);
  // 用户数据
  const userDataSVG = textToSVGFn(`写了${words}个字   收获${likes}个赞`);
  const userNameBuffer = await sharp(new Buffer(userNameSVG)).toBuffer({
    resolveWithObject: true
  });
  const userDataBuffer = await sharp(new Buffer(userDataSVG)).toBuffer({
    resolveWithObject: true
  });

  const buffers = [avatarBuffer, qrCodeBuffer, userNameBuffer, userDataBuffer];
  // 图层叠加参数列表
  const overlayOptions = [
    { top: 150, left: 230 },
    { top: 861, left: 227 },
    {
      top: 365,
      left: (backgroundImageInfo.info.width - userNameBuffer.info.width) / 2
    },
    {
      top: 435,
      left: (backgroundImageInfo.info.width - userDataBuffer.info.width) / 2
    }
  ];

  // 组合多个图层:图片+文字图层
  return buffers
    .reduce((input, overlay, index) => {
      return input.then(result => {
        console.dir(overlay.info);
        return sharp(result.data)
          .overlayWith(overlay.data, overlayOptions[index])
          .toBuffer({ resolveWithObject: true });
      });
    }, backgroudBuffer)
    .then((data) => {
      return sharp(data.data).toFile(outFilePath);
    }).catch(error => {
      throw new Error('Generate Share Image Failed.');
    });
}

/**
 * 生成圆形的头像
 * @param {*} avatarPath 头像路径
 */
function genCircleAvatar(avatarPath) {
  return sharp(avatarPath)
    .resize(180, 180)
    .overlayWith(roundedCorners, { cutout: true })
    .png()
    .toBuffer({
      resolveWithObject: true
    });
}

module.exports = {
  genShareImage
};

三、阿宝哥有话说

3.1 如何区分图片的类型

计算机并不是通过图片的后缀名来区分不同的图片类型,而是通过 “魔数”(Magic Number)来区分。 对于某一些类型的文件,起始的几个字节内容都是固定的,跟据这几个字节的内容就可以判断文件的类型。

常见图片类型对应的魔数如下表所示:

文件类型文件后缀魔数
JPEGjpg/jpeg0xFFD8FF
PNGpng0x89504E47
GIFgif0x47494638(GIF8)
BMPbmp0x424D

这里我们以阿宝哥的头像(abao.png)为例,验证一下该图片的类型是否正确:

在日常开发过程中,如果遇到检测图片类型的场景,我们可以直接利用一些现成的第三方库。比如,你想要判断一张图片是否为 PNG 类型,这时你可以使用 is-png 这个库,它同时支持浏览器和 Node.js,使用示例如下:

Node.js

// npm install read-chunk
const readChunk = require('read-chunk'); 
const isPng = require('is-png');
const buffer = readChunk.sync('unicorn.png', 0, 8);

isPng(buffer);
//=> true

Browser

(async () => {
	const response = await fetch('unicorn.png');
	const buffer = await response.arrayBuffer();

	isPng(new Uint8Array(buffer));
	//=> true
})();
3.2 如何获取图片的尺寸

图片的尺寸、位深度、色彩类型和压缩算法都会存储在文件的二进制数据中,我们继续以阿宝哥的头像(abao.png)为例,来了解一下实际的情况:

528(十进制) => 0x0210

560(十进制)=> 0x0230

因此如果想要获取图片的尺寸,我们就需要依据不同的图片格式对图片二进制数据进行解析。幸运的是,我们不需要自己做这件事,image-size 这个 Node.js 库已经帮我们实现了获取主流图片类型文件尺寸的功能:

同步方式

var sizeOf = require('image-size');

var dimensions = sizeOf('images/abao.png');
console.log(dimensions.width, dimensions.height);

异步方式

var sizeOf = require('image-size');

sizeOf('images/abao.png', function (err, dimensions) {
  console.log(dimensions.width, dimensions.height);
});

image-size 这个库功能还是蛮强大的,除了支持 PNG 格式之外,还支持 BMP、GIF、ICO、JPEG、SVG 和 WebP 等格式。

3.3 如何预览本地图片

利用 HTML FileReader API,我们也可以方便的实现图片本地预览功能,具体代码如下:

<input type="file" accept="image/*" onchange="loadFile(event)">
<img id="output"/>
<script>
  const loadFile = function(event) {
    const reader = new FileReader();
    reader.onload = function(){
      const output = document.querySelector('output');
      output.src = reader.result;
    };
    reader.readAsDataURL(event.target.files[0]);
  };
</script>

在完成本地图片预览之后,可以直接把图片对应的 Data URLs 数据提交到服务器。针对这种情形,服务端需要做一些相关处理,才能正常保存上传的图片,这里以 Express 为例,具体处理代码如下:

const app = require('express')();

app.post('/upload', function(req, res){
    let imgData = req.body.imgData; // 获取POST请求中的base64图片数据
    let base64Data = imgData.replace(/^data:image\/\w+;base64,/, "");
    let dataBuffer = Buffer.from(base64Data, 'base64');
    fs.writeFile("image.png", dataBuffer, function(err) {
        if(err){
          res.send(err);
        }else{
          res.send("图片上传成功!");
        }
    });
});
3.4 如何实现图片压缩

在一些场合中,我们希望在上传本地图片时,先对图片进行一定的压缩,然后再提交到服务器,从而减少传输的数据量。在前端要实现图片压缩,我们可以利用 Canvas 对象提供的 toDataURL() 方法,该方法接收 type 和 encoderOptions 两个可选参数。

其中 type 表示图片格式,默认为 image/png。而 encoderOptions 用于表示图片的质量,在指定图片格式为 image/jpeg 或 image/webp 的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92,其他参数会被忽略。

下面我们来看一下具体如何实现图片压缩:

function compress(base64, quality, mimeType) {
  let canvas = document.createElement("canvas");
  let img = document.createElement("img");
  img.crossOrigin = "anonymous";
  return new Promise((resolve, reject) => {
    img.src = base64;
    img.onload = () => {
      let targetWidth, targetHeight;
      if (img.width > MAX_WIDTH) {
        targetWidth = MAX_WIDTH;
        targetHeight = (img.height * MAX_WIDTH) / img.width;
      } else {
        targetWidth = img.width;
        targetHeight = img.height;
      }
      canvas.width = targetWidth;
      canvas.height = targetHeight;
      let ctx = canvas.getContext("2d");
      ctx.clearRect(0, 0, targetWidth, targetHeight); // 清除画布
      ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
      let imageData = canvas.toDataURL(mimeType, quality / 100);
      resolve(imageData);
    };
  });
}

对于返回的 Data URL 格式的图片数据,为了进一步减少传输的数据量,我们可以把它转换为 Blob 对象:

function dataUrlToBlob(base64, mimeType) {
  let bytes = window.atob(base64.split(",")[1]);
  let ab = new ArrayBuffer(bytes.length);
  let ia = new Uint8Array(ab);
  for (let i = 0; i < bytes.length; i++) {
    ia[i] = bytes.charCodeAt(i);
  }
  return new Blob([ab], { type: mimeType });
}

在转换完成后,我们就可以压缩后的图片对应的 Blob 对象封装在 FormData 对象中,然后再通过 AJAX 提交到服务器上:

function uploadFile(url, blob) {
  let formData = new FormData();
  let request = new XMLHttpRequest();
  formData.append("image", blob);
  request.open("POST", url, true);
  request.send(formData);
}
3.5 如何操作位图像素数据

如果想要操作图片像素数据,我们可以利用 CanvasRenderingContext2D 提供的 getImageData 来获取图片像素数据,其中 getImageData() 返回一个 ImageData 对象,用来描述 canvas 区域隐含的像素数据,这个区域通过矩形表示,起始点为(sx, sy)、宽为 sw、高为 sh。其中 getImageData 方法的语法如下:

ctx.getImageData(sx, sy, sw, sh);

相应的参数说明如下:

  • sx:将要被提取的图像数据矩形区域的左上角 x 坐标。
  • sy:将要被提取的图像数据矩形区域的左上角 y 坐标。
  • sw:将要被提取的图像数据矩形区域的宽度。
  • sh:将要被提取的图像数据矩形区域的高度。

在获取到图片的像素数据之后,我们就可以对获取的像素数据进行处理,比如进行灰度化或反色处理。当完成处理后,若要在页面上显示处理效果,则我们需要利用 CanvasRenderingContext2D 提供的另一个 API —— putImageData。

该 API 是 Canvas 2D API 将数据从已有的 ImageData 对象绘制到位图的方法。 如果提供了一个绘制过的矩形,则只绘制该矩形的像素。此方法不受画布转换矩阵的影响。putImageData 方法的语法如下:

void ctx.putImageData(imagedata, dx, dy);
void ctx.putImageData(imagedata, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight);

相应的参数说明如下:

  • imageData: ImageData ,包含像素值的数组对象。
  • dx:源图像数据在目标画布中的位置偏移量(x 轴方向的偏移量)。
  • dy:源图像数据在目标画布中的位置偏移量(y 轴方向的偏移量)。
  • dirtyX(可选):在源图像数据中,矩形区域左上角的位置。默认是整个图像数据的左上角(x 坐标)。
  • dirtyY(可选):在源图像数据中,矩形区域左上角的位置。默认是整个图像数据的左上角(y 坐标)。
  • dirtyWidth(可选):在源图像数据中,矩形区域的宽度。默认是图像数据的宽度。
  • dirtyHeight(可选):在源图像数据中,矩形区域的高度。默认是图像数据的高度。

介绍完相关的 API,下面我们来举一个实际例子:

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>图片反色和灰度化处理</title>
  </head>
  <body onload="loadImage()">
    <div>
      <button id="invertbtn">反色</button>
      <button id="grayscalebtn">灰度化</button>
    </div>
    <canvas id="canvas" width="800" height="600"></canvas>
    <script>
      function loadImage() {
        var img = new Image();
        img.crossOrigin = "";
        img.onload = function () {
          draw(this);
        };
        // 这是阿宝哥的头像哟
        img.src = "https://avatars3.githubusercontent.com/u/4220799";
      }

      function draw(img) {
        var canvas = document.getElementById("canvas");
        var ctx = canvas.getContext("2d");
        ctx.drawImage(img, 0, 0);
        img.style.display = "none";
        var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        var data = imageData.data;

        var invert = function () {
          for (var i = 0; i < data.length; i += 4) {
            data[i] = 255 - data[i]; // red
            data[i + 1] = 255 - data[i + 1]; // green
            data[i + 2] = 255 - data[i + 2]; // blue
          }
          ctx.putImageData(imageData, 0, 0);
        };

        var grayscale = function () {
          for (var i = 0; i < data.length; i += 4) {
            var avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
            data[i] = avg; // red
            data[i + 1] = avg; // green
            data[i + 2] = avg; // blue
          }
          ctx.putImageData(imageData, 0, 0);
        };

        var invertbtn = document.getElementById("invertbtn");
        invertbtn.addEventListener("click", invert);
        var grayscalebtn = document.getElementById("grayscalebtn");
        grayscalebtn.addEventListener("click", grayscale);
      }
    </script>
  </body>
</html>

需要注意的在调用 getImageData 方法获取图片像素数据时,你可能会遇到跨域问题,比如:

Uncaught DOMException: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data.

对于这个问题,你可以阅读 张鑫旭 大神 “解决canvas图片getImageData,toDataURL跨域问题” 这一篇文章。

3.6 如何实现图片隐写

隐写术是一门关于信息隐藏的技巧与科学,所谓信息隐藏指的是不让除预期的接收者之外的任何人知晓信息的传递事件或者信息的内容。 隐写术的英文叫做 Steganography,来源于特里特米乌斯的一本讲述密码学与隐写术的著作 Steganographia,该书书名源于希腊语,意为 “隐秘书写”。

下图是阿宝哥采用在线的图片隐写工具,将 “全栈修仙之路” 这 6 个字隐藏到原始的图片中,然后使用对应的解密工具,解密出隐藏信息的结果:

(在线图片隐写体验地址:https://c.p2hp.com/yinxietu/)

目前有多种方案可以实现图片隐写,以下是几种常见的方案:

  • 附加式的图片隐写;
  • 基于文件结构的图片隐写;
  • 基于 LSB 原理的图片隐写;
  • 基于 DCT 域的 JPG 图片隐写;
  • 数字水印的隐写;
  • 图片容差的隐写。

篇幅有限,这里我们就不继续展开,分别介绍每种方案,感兴趣的小伙伴可以阅读 “隐写术之图片隐写(一)” 这篇文章。

四、参考资源


加入全栈修仙之路 交流群,阿里、腾讯和京东大佬都在的群里,备注 3。
在这里插入图片描述

vue3中使用组件tui-image-editor进行图片处理 vue3中使用tui-image-editor对图片进行处理 阅读详情

相关推荐

Image-Editor

图片编辑器 ㅤㅤㅤㅤㅤㅤㅤ ㅤㅤㅤㅤㅤㅤㅤ 一个开源的图像编辑器,可以以多种方式处理图像! 如果您需要回购中的更多模式或发现任何错误,请在@ TeleRoid14中提及 安装 部署到Heroku 在您的vps中部署 git clone https://github.com/P-Phreak/Image-Editor cd Image-Editor pip3 install -r requirements.txt # <Create> python3 bot.py 设定档 TG_BOT_TOKEN-从@BotFather获取机器人令牌 APP_ID-来自my.telegram.org(或@UseTGXBot) API_HASH-来自my.telegram.org(或@UseTGXBot) RemoveBG_API-从RemoveBG

vue中使用图像编辑器tui-image-editor(一)

图片

weixin_44867717的博客 7007

用Typora画流程图和序列图:Mermaid语法实战教程(含常见报错解决)

本文是一份详细的Typora与Mermaid语法实战教程,教你如何在Markdown文档中高效绘制流程图和序列图。文章涵盖从环境配置、核心语法到复杂图表构建的全过程,并重点解析了常见报错原因与解决方案,助你将可视化图表无缝融入文档工作流。

weixin_29279101的博客 299

一个关于image访问图片跨域的问题

通过 'img' 加载的图片,浏览器默认情况下会将其缓存起来。当我们从 JS 的代码中创建的 'img' 再去访问同一个图片时,浏览器就不会再发起新的请求,而是直接访问缓存的图片。但是由于 JS 中的 'img' 设置了 crossorigin,也就意味着它将要以 CORS 的方式请求,但缓存中的图片显然不是的,所以浏览器直接就拒绝了。连网络请求都没有发起。

weixin_42333548的博客 9652

VASP+机器学习快速收敛AIMD

本文介绍了如何利用VASP结合机器学习技术快速收敛AIMD(从头算分子动力学)模拟。首先,通过训练机器学习力场,生成关键文件如ML_ABN(包含结构数据集)、ML_FFN(存储回归结果)和ML_LOGFILE(记录训练日志)。接着,配置INCAR文件以设置计算参数,包括并行化、分子动力学和机器学习相关参数。KPOINTS文件用于定义K点网格。计算完成后,备份CONTCAR文件作为后续模拟的初始结构。最后,利用训练好的力场文件进行纯机器学习力场分子动力学模拟,并提交任务执行。

qq_44812865的博客 1405

Vcenter虚拟机磁盘转换实战:如何用StarWind V2V将vmdk转为qcow2(附空间优化技巧)

本文详细介绍了如何使用StarWind V2V Converter将Vcenter中的vmdk虚拟机磁盘高效转换为qcow2格式,并提供了空间优化技巧。从准备工作、导出vmdk文件到转换过程中的性能调优,帮助管理员在迁移虚拟机时减少资源消耗,确保数据完整性和性能。

dell8的博客 203

解决:百度编辑器Ueditor跨域上传图片

问题:百度编辑器上传图片到本项目的时候,没有问题,一旦跨域上传图片的时候就会导致图片上传错误,原因是因为百度编辑器使用的编辑区域是IFrame,而父窗口和主窗口的数据不可跨域调用. 解决方案:将百度编辑器的图片数据提交到本项目,获取到图片的字节文件,接着使用远程方法调用将字节数据发送到图片项目存储到硬盘,存储成功之后,将图片的引用URL及其他信息以RMI的返回值返回,在本项目中将接收的上传成功的...

weixin_30433075的博客 575

小蔺的米哈游数据分析师之路——MYSQL篇

作为新人,记录自己mysql学习历程

weixin_47070228的博客 434

node 压缩图片_推荐10个常用的图片处理小帮手(下)「值得收藏」

作者: semlinker转发链接:https://mp.weixin.qq.com/s/i3ynTtPJOECoAYfqHFoo3Q前言本文给小伙伴们隆重介绍用于图片处理的十个 「“小帮手”」,他们各个身怀绝技,拥有模糊、压缩、裁剪、旋转、合成、比对等技能。相信认识他们之后,你将能够轻松应对大多数的图片处理场景。该章节你将会学到以下知识:如何区分图片的类型(可文件后缀名);如何获取图片的尺寸(可...

weixin_39523835的博客 690

华硕三频万兆分布式路由组网实战:从AiMesh 2.0到全屋Wi-Fi 6覆盖

Mesh组网是解决大户型Wi-Fi覆盖难题的主流方案,其核心原理是通过多个节点构建网状网络,实现设备间的无缝漫游。在技术层面,Wi-Fi 6标准、三频设计(包括专属回程频段)和有线回程等技术,共同提升了网络的带宽、稳定性和抗干扰能力。这些技术的工程价值在于,能够为多设备并发、高带宽应用(如4K/8K流媒体、在线游戏、内网文件高速传输)提供可靠支撑。具体到应用场景,例如利用华硕AiMesh 2.0系统,配合三频万兆分布式路由器,可以实现智能的设备连接与负载均衡。通过合理规划节点位置、优先采用有线回程,并进行Q

weixin_34054866的博客 431

AD学习笔记(一)基础认识

文章目录AD学习笔记第一讲 AD基础认识一、两库两图(分别对应下图)1、PCB图(pcbdoc)2、原理图(schdoc)3、原理图库(schlib)4、PCB库(pcblib)二、封装1、DIP封装2、LQFP封装三、绘制整体流程1、建立工程(PrjPcb)2、建立两库两图并保存整个工程(五次)3、从库中提取绘制原理图4、原理图导入pcb图5、根据原理图布线6、pcb图交由工厂打板 AD学习笔记 第一讲 AD基础认识 一、两库两图(分别对应下图) PCB图(pcbdoc) 原理图(schdoc) PCB库

qq_52465473的博客 6686

简单好用的在线工具平台,实用小帮手 #腾讯帮小忙

文档格式转换、简单数据计算、快速抠图...这些办公时必备技能帮小忙在线就能轻松搞定!什么是帮小忙?帮小忙是腾讯近期推出的一款免费在线工具平台,功能强大,涵盖各种办公可能用到的内容,一个平台就可搞定,活脱脱的一个互联网电子保姆!图源:帮小忙官网帮小忙能提供哪些服务?- 图片处理图源:帮小忙官网- 数据换算图源:帮小忙官网- 生活娱乐图源:帮小忙官网- 教育领域图源:帮小忙官网- 文本转换图源:帮小忙...

shadowcz007的博客 1354

第八章 GPU Scheduler 分析:8.3 drm_sched_entity — 调度实体

本文介绍了DRM GPU调度器中的drm_sched_entity结构,它作为用户提交命令进入调度器的提交通道,解决了归属、排序和公平性问题。文章首先分析了命令提交的归属需求,然后详细阐述了entity的核心模型,包括其职责、进程映射关系和核心字段。接着说明了用户态提交进入内核后的关键节点路径,以及entity内部的FIFO语义和激活机制。entity通过组织同一context的命令提交包为FIFO队列,使调度器能够在多个entity之间进行优先级和公平性调度,同时保证同一提交通道的顺序执行。

图形/计算GPU的驱动与虚拟化技术分享园地 655

藏经阁导航-做你的全能小帮手

小伙伴们注意:公众号的推送机制不再按照时间前后推送了,微信公众号信息流乱序。君哥建议大家把公众号置顶(设为星标★),以便第一时间看到推送,方法如下图:万水千山总是情,为君哥三连行不行...

科技毒瘤君 12万+

DSI3接口介绍

DSI3(distributed system interface)分布式系统接口,是一种强大且通用的总线协议,旨在将多个遥感器和执行器互连到中央控制模块。在单总线中集成了电压信号、电流信号还有电源,甚至引用了3位状态机概念(反向数据),从逻辑0/1扩展到了0/1/2。

Airer_00的博客 4343

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

【工具】788- 这些开源项目,让你轻松应对十大工作场景

俗话说得好,工欲善其事必先利其器。本文阿宝哥将介绍一些优秀的开源项目,利用这些开源项目,你将能能轻松应对以下十个工作场景:文件上传、图片处理、文档处理、网络请求、数据存储、微前端、表单设...

pingan8787 992

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

MTools实战体验:图片处理+音视频编辑+AI工具,一站式解决方案

本文介绍了如何在星图GPU平台上一键自动化部署HG-ha/MTools开箱即用镜像,体验集图片处理、音视频编辑与AI工具于一体的桌面应用。该镜像整合了智能抠图、超分辨率放大、格式转换及文本转语音等核心功能,用户可在一个软件内高效完成从图片修复到短视频制作的完整工作流,显著提升日常创作效率。

weixin_42298164的博客 66

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

秒哒-百度新推出0代码AI开发平台,一句话即可生成应用

“秒哒”是百度基于其文心大模型推出的生成式应用开发平台,旨在通过自然语言交互和智能体协作,降低应用开发门槛,让非程序员用户也能快速构建复杂应用。作为AI原生开发平台的革新标杆,秒哒以"自然语言即生产力"为核心理念,通过大模型驱动的智能体系统,实现基于自然语言指令的零代码应用构建。其革命性架构支持多智能体任务协同与工具链智能调度,开创了低门槛智能开发新范式。秒哒官网首页。

ideal_wk的博客 1439

免费图片拼接软件有哪些?旅游爱好者的编辑小帮手

以上就是我为大家整理的关于图片拼接工具有哪些的全部介绍了,这些软件各有千秋,但它们共同的特点就是能够极大地提高你编辑图片的效率和质量,试试看,你一定会爱上这些图片编辑小助手的!这款软件提供了丰富的海报模板和强大的编辑功能,操作界面简洁直观,操作起来非常顺手。这款软件的对齐功能和图层功能为使用者提供了优化拼接效果的可能,适合需要精细调整的使用者。这款软件的操作难度相对较高,但它提供了详细的教程和指南,帮助使用者掌握其高级功能。它更像是一个创意社区,提供了丰富的滤镜和拼接工具,适合喜欢DIY的使用者。

huduokyou的博客 407

宇宙工具箱重磅回归

更新一次,Bug 一次” 的神奇软件,宇宙工具箱又大摇大摆地回来了!▶ 还记得之前我给大伙推荐过的功能逆天的宇宙工具箱 App 不?这 App 啥都好,就是开发者好像对强制更新着了魔,旧版本动不动就失效,搞得好多用户那叫一个头大。▶ 前段时间老版本又 “光荣下岗” 了。为了让大家能接着享受这强大工具带来的便利,我昨晚可是挑灯夜战,终于给大家搞到了 v2.7.1 终身会员版,而且不用登录就能直接用所有会员功能,是不是超棒!▶ 废话不多说哈,要是你之前用过这软件,直接滑到文章底部下载最新版就行;

wzk1681106的博客 1115

蜗牛工具箱:你的全能生活助手

蜗牛工具箱:你的全能生活助手 项目介绍 在快节奏的现代生活中,我们常常需要各种工具来提高效率、简化操作。今天,我要向大家推荐一款功能强大、操作简便的工具箱应用——蜗牛工具箱。这款应用不仅涵盖了生活实用、效率办公、图片处理等多个领域,还隐藏了许多VIP功能,等待你去探索。无论你是学生、上班族还是自由职业者,蜗牛工具箱都能为你提供极大的便利。 项目技术分析 蜗牛工具箱的核心技术框架基于Android传...

gitblog_00819的博客 700

效率神器!这个免费在线工具网站,程序员和办公族都爱用

在这个信息爆炸的时代,效率就是竞争力。ToolX.site正是那个帮你省下无数琐碎时间的“数字瑞士军刀”。无论你是程序员、设计师、学生还是行政人员,都能在这里找到提升效率的利器。,下次遇到小任务,别再手忙脚乱了——让工具为你工作!你用过哪些好用的在线工具?欢迎在评论区分享你的宝藏网站!

ghtwf01的博客 429

HG-ha/MTools快速上手:图片/音视频/AI开发四合一工具实操手册

本文介绍了如何在星图GPU平台上自动化部署HG-ha/MTools 开箱即用镜像,快速启用本地化AI音视频处理能力。用户无需配置环境,即可实现会议录音自动添加字幕等典型场景,兼顾隐私安全与GPU加速性能,显著提升内容创作与开发效率。

weixin_35749796的博客 293

MTools开箱即用:5个超实用功能,快速提升你的工作效率

本文介绍了如何在星图GPU平台上自动化部署HG-ha/MTools开箱即用镜像,实现高效办公工具集的一键搭建。该镜像集成智能图片处理、音视频编辑等五大实用功能,特别适合快速完成AI图片生成、老照片修复等多媒体处理任务,显著提升工作效率。

weixin_31591833的博客 334

MTools效果展示:看看这个跨平台桌面工具如何提升你的工作效率

本文介绍了如何在星图GPU平台上自动化部署HG-ha/MTools开箱即用镜像,实现跨平台桌面工具的高效应用。该镜像集成了图片处理、音视频编辑和AI工具等功能,特别适合快速完成图片智能修复、风格转换等任务,显著提升内容创作效率。

weixin_42388898的博客 22

HG-ha/MTools保姆级教程:图片/音视频/AI开发全功能桌面工具快速上手

本文介绍了如何在星图GPU平台上自动化部署HG-ha/MTools开箱即用镜像,快速获得一个集图片处理、音视频编辑与AI开发于一体的全能桌面工具。该镜像内置多种AI模型,用户无需复杂配置即可直接使用,例如通过其AI图片超分辨率功能,轻松将模糊图片修复为高清大图,显著提升视觉内容质量。

weixin_35238815的博客 310
上一篇: 前端进阶:跟着开源项目学习插件化架构
下一篇: 你不知道的 Web Workers (上)[7.8K 字 | 多图预警]
阿宝哥​_
博客等级 码龄15年 56粉丝 9原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值