图像操作API

图像操作API用于在执行中操作API,及自动化结果的验证,主要通过Image对象。此外虚拟控件也有相应的方法,根据图片实现判断。

Image对象

Image对象用于获得图片的属性信息,及针对图片的比较操作。

Image对象定义如下:

  1. class Image {
  2. width: number;
  3. height: number;
  4. clip(rect: {x: number, y: number, width: number, height: number}): Image;
  5. drawImage(image: Image, x: number, y: number): Image;
  6. getData(option?: {encoding: 'base64' | null}): Promise<Buffer | string>;
  7. static fromData(bufferOrString: Buffer | string): Promise<Image>;
  8. static fromFile(filePath: string): Promise<Image>;
  9. static imageEqual(image1: Buffer | string | Image,
  10. image2: Buffer | string | Image,
  11. options?: CompareOptions,
  12. compareInfo?: ImageCompareInfo): Promise<boolean>;
  13. static imageCompare(image1: Buffer | string | Image,
  14. image2: Buffer | string | Image,
  15. options?: CompareOptions): Promise<ImageCompareResult>
  16. }

获得一个Image对象的实例可以得到图片的属性,或将图片用于后继处理。

可以从Buffer、base64的字符串,或者一个文件路径生成Image实例,例如:

  1. const { Image } = require('leanpro.visual');
  2. const fs = require('fs');
  3. (async function() {
  4. let buf1 = fs.readFileSync('c:/temp/image1.png');
  5. let image1 = await Image.fromData(buf1);
  6. console.log(image1.width, image1.height);
  7. let image2 = await Image.fromFile('c:/temp/image2.png');
  8. console.log(image2.width, image2.height);
  9. })()

上面例子分别从Buffer和文件中读取图片数据并打印宽度和高度。

注意:无法直接通过new Image()创建对象,需要通过fromData或者fromFile生成Image实例。另外Image对象现只支持PNG格式的图片。

实例成员:

  • clip 用于剪切图片,返回另一个图片。传入矩形框,按照矩形框剪切图片,如果传入数据不合法导致剪切的图片宽度或高度为0,返回为null;
  • drawImage 用于将两幅绘制图片绘制到一幅图片上。返回为新绘制的图片。

例如:

  1. this.screen1 = await model.getButton("Five").takeScreenshot();
  2. this.screen2 = await model.getButton("Six").takeScreenshot();
  3. let image1 = await Image.fromData(this.screen1);
  4. let image2 = await Image.fromData(this.screen2);
  5. image1 = image1.clip({ x: 10, y: 10, width: image1.width - 20, height: image1.height - 20 })
  6. image2 = image2.clip({ x: 10, y: 10, width: image2.width - 20, height: image2.height - 20 })
  7. let combinedImage = image1.drawImage(image2, image1.width + 5, 0);

上面代码将两个按钮截屏剪切掉10像素的周围边缘,然后绘制到一张图片上,中间间隔5个像素。

如果你想让两幅图片上下排列,你可以使用类似下面的代码:

  1. let combinedImage = image1.drawImage(image2, 0, image1.height + 5);

您还可以使用为负值的x,y,以便第二个图像在第一个图像的另一侧绘制,例如,以下语句将在左侧绘制image2,在右侧绘制image1:

  1. let combinedImage = image1.drawImage(image2, -(image1.width + 5), 0);
  • getData返回图片的内容数据,可用于保存至文件,或添加到报表中。注意它返回的是Promise,所以在async函数中需要加await。

图片比较操作

在自动化过程中,为了验证结果的正确性,可以通过验证结果图片和期望图片的差异来判断执行结果是否正确。

图片判断主要有两种方式:

  • 比较图片差异,返回布尔值。
  • 将两幅图对应像素相减,产生结果图片,将差异点用色彩标出来,结果图片显示在报表中,可以由人工判断是否正确。

Image对象的imageEqual和imageCompare分别针对上面两种对比方式。

由于操作系统、分辨率、颜色设置都可能影响图片的显示,同样的控件在不同的环境设置下可能显示不同,所以在比较截屏图片时,一般会设置比较容忍度。在容忍度阈值内认为两幅图片是相同的。容忍度有如下的分类:

  • 颜色容忍度:针对两幅图片的两个对应像素点,如果它们的RGB颜色按照特定的距离算法在一定的范围内认为是相同的。
  • 像素容忍度:针对两幅图整体而言,如果不同的像素点数量在一定的范围内,则认为两幅图是相同的。像素容忍度还可以根据百分比来设定,即不同的点占图片所有像素的百分比。

  • imageEqual

imageEqual用来比较两幅图片的差异,返回布尔值的Promise,如果是true表示相同,false表示不同。如果您只想知道两个图像是否不同,请使用此API。 如果您想知道获取显示差异所在位置的结果图像,请使用另一个API imageCompare

它的函数签名:

  1. imageEqual(image1: Buffer | string | Image,
  2. image2: Buffer | string | Image,
  3. options?: CompareOptions,
  4. compareInfo?: ImageCompareInfo): Promise<boolean>;
  • image1、image2可以是Buffer、base64的字符串,或者一个Image实例。
  • options用来指定图片比较的参数,它有如下的参数设置:
  1. interface CompareOptions {
  2. colorTolerance?: number, //default to 0.1
  3. pixelNumberTolerance?: number, //no default value
  4. pixelPercentTolerance?: number, //default 1, means 1%
  5. ignoreExtraPart?: boolean //default to false
  6. }

其中:

  1. - **colorTolerance**:颜色容忍度,缺省为0.1。一般不用修改。
  2. - **pixelNumberTolerance**:像素数量容忍度,没有缺省值。
  3. - **pixelPercentTolerance**:像素百分比容忍度,缺省为1,即1%。例如,两幅图片分别有10000像素,如果允许少于150个像素不同的情况下认为图片相同,则设置为1.5
  4. - **ignoreExtraPart**:是否忽略非重叠部分,缺省为false。两幅比较图片可以是不同尺寸,比较时左上角对齐,超出部分会当成是不同的点。如果设成true,则会忽略超出部分。

当pixelNumberTolerance和pixelPercentTolerance有一个超出了设定值(或缺省值)比较就会返回false。如果只需要使用其中一个设定,可以将另一个设定设为比较大的值。例如只需要pixelNumberTolerance,并忽略pixelPercentTolerance,可以把pixelPercentTolerance设成100。

  • 除了返回值表示是否相同外,有时需要知道详细的信息,例如像素尺寸,有多少点不同,不同的点的百分比等,这时可以通过传出compareInfo对象获得相关信息。

compareInfo的类型:

  1. interface ImageCompareInfo {
  2. image1: {
  3. width: number,
  4. height: number
  5. },
  6. image2: {
  7. width: number,
  8. height: number
  9. }
  10. diffPixels: number,
  11. diffPercentage: number
  12. }

使用时,如果compareInfo传入一个空的对象,调用imageEqual后compareInfo会填充如上结构的数据。

下面是调用样例:

  1. (async function() {
  2. try {
  3. let pngSource = await Image.fromFile(__dirname + '/../source.png');
  4. let pngTargt = await Image.fromFile(__dirname + '/../target.png');
  5. let compareInfo = {};
  6. let isEqual = await Image.imageEqual(pngSource, pngTargt,
  7. {pixelNumberTolerance: 300, ignoreExtraPart: true}, compareInfo);
  8. console.log('isEqual', isEqual, JSON.stringify(compareInfo, null, 2));
  9. } catch(err) {
  10. console.log(err)
  11. }
  12. })()

执行时它传入两个图片,比较是否相同,同时会忽略没有重叠部分。如果不同的像素点超过300个,则返回false,同时它返回了compareInfo的详细数据。打印的内容如下:

  1. isEqual false {
  2. "image1": {
  3. "width": 375,
  4. "height": 266
  5. },
  6. "image2": {
  7. "width": 402,
  8. "height": 224
  9. },
  10. "diffPixels": 3502,
  11. "diffPercentage": 3.3100814760203408
  12. }

  • imageCompare

impageCompare,比较两者图片,返回详细信息的数据,特别是两幅图片差异部分的图片。如果您想获得显示差异所在位置的结果图像,请使用此API。

它有如下的签名:

  1. imageCompare(image1: Buffer | string | Image,
  2. image2: Buffer | string | Image,
  3. options?: CompareOptions): Promise<ImageCompareResult>
  • image1、image2可以是Buffer、base64的string,或者一个Image实例。
  • options 的设置与imageEqual的options设置相同。此外,如果ignoreExtraPart是false,对生成的差分图片有影响。如果ignoreExtraPart为false,图片非重叠部分会设置为红色,否则非重叠不设置颜色。返回值是名为ImpageCompareResult的结构:
  1. interface ImageCompareResult {
  2. equal: boolean,
  3. info: ImageCompareInfo,
  4. diffImage: Image
  5. }

其中:

  • equal表示是否根据容忍度设置认为图片相同。
  • info的结构与imageEqual的compareInfo结构相同,包括像素尺寸,有多少点不同,不同的点的百分比等。
  • diffImage是返回的差分图片的Image对象,相同的像素点以缺省白色表示,不同的点显示为红色。图像中原有的图案会以浅色显示在目标图片中,方便定位差异部分所在的位置。

这两个图片比较API中,imageEqual是imageCompare的封装,为了更直观的返回两个图片是否相等的判断结果。如果只想知道是否相等就用imageEqual,如果除了是否相等外,还想知道更详细的就用imageCompare。

示例

下面是比较两个图片并生成差分图片的样例:

  1. (async function() {
  2. try {
  3. let pngSource = await Image.fromFile(__dirname + '/../image1.png');
  4. let pngTargt = await Image.fromFile(__dirname + '/../image2.png');
  5. let result = await Image.imageCompare(pngSource, pngTargt, {pixelNumberTolerance: 300});
  6. let diffImage = result.diffImage;
  7. console.log('resultMeta', JSON.stringify(result.info, null, 2));
  8. let imageData = await diffImage.getData();
  9. fs.writeFileSync(__dirname + '/../diff.png', imageData)
  10. } catch(err) {
  11. console.log(err)
  12. }
  13. })()

该样例打印出差异信息,并把结果图片保存成文件"diff.png"。

假如我们有下面两幅图片:

image1 (375 266)image2 (402 224)
图像操作API - 图1图像操作API - 图2

根据参数的不同,可以生成下面的差分图片:

ignoreExtraPart = trueignoreExtraPart = false
图像操作API - 图3图像操作API - 图4

左边是ignoreExtraPart = true的情况,超出部分也标记为红色,右边是ignoreExtraPart = false,忽略了超出部分。

控件与模型图片比较

Windows自动化中一个常见的场景是,将运行时控件的截屏与模型中的保存的测试对象截屏相比较。这可以通过调用测试对象的modelImage方法获得测试对象的截屏png图片,以base64字符串数据返回。如果模型中该对象没有对应的截屏图片,则返回null。

下面的样例从对象模型中获取按钮对象,同时拿到控件截屏和模型中的截屏,并做对比,将对比信息打印出来,同时将差分图片保存到目录中。

  1. const { Image } = require('leanpro.visual');
  2. const { TestModel } = require('leanpro.win');
  3. const fs = require('fs');
  4. const model = TestModel.loadModel(__dirname + "\\test.tmodel");
  5. (async function () {
  6. let five = model.getButton("Five");
  7. let controlImage = await five.takeScreenshot();
  8. let modelImage = await five.modelImage();
  9. let result = await Image.imageCompare(modelImage, controlImage);
  10. fs.writeFileSync(__dirname + '/diff.png', await result.diffImage.getData());
  11. //print the diff information
  12. console.log(result.info)
  13. })();

虚拟控件上的图片操作

虚拟控件的图片相关操作有:

  • findSnapshot
  • matchSnapshot

请参见虚拟控件API获得相关帮助。