xref: /aosp_15_r20/external/pdfium/testing/tools/safetynet_image.py (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
1*3ac0a46fSAndroid Build Coastguard Worker# Copyright 2017 The PDFium Authors
2*3ac0a46fSAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
3*3ac0a46fSAndroid Build Coastguard Worker# found in the LICENSE file.
4*3ac0a46fSAndroid Build Coastguard Worker"""Compares pairs of page images and generates an HTML to look at differences.
5*3ac0a46fSAndroid Build Coastguard Worker"""
6*3ac0a46fSAndroid Build Coastguard Worker
7*3ac0a46fSAndroid Build Coastguard Workerimport functools
8*3ac0a46fSAndroid Build Coastguard Workerimport glob
9*3ac0a46fSAndroid Build Coastguard Workerimport multiprocessing
10*3ac0a46fSAndroid Build Coastguard Workerimport os
11*3ac0a46fSAndroid Build Coastguard Workerimport re
12*3ac0a46fSAndroid Build Coastguard Workerimport subprocess
13*3ac0a46fSAndroid Build Coastguard Workerimport sys
14*3ac0a46fSAndroid Build Coastguard Workerimport webbrowser
15*3ac0a46fSAndroid Build Coastguard Worker
16*3ac0a46fSAndroid Build Coastguard Workerfrom common import DirectoryFinder
17*3ac0a46fSAndroid Build Coastguard Worker
18*3ac0a46fSAndroid Build Coastguard Worker
19*3ac0a46fSAndroid Build Coastguard Workerdef GenerateOneDiffParallel(image_comparison, image):
20*3ac0a46fSAndroid Build Coastguard Worker  return image_comparison.GenerateOneDiff(image)
21*3ac0a46fSAndroid Build Coastguard Worker
22*3ac0a46fSAndroid Build Coastguard Worker
23*3ac0a46fSAndroid Build Coastguard Workerclass ImageComparison:
24*3ac0a46fSAndroid Build Coastguard Worker  """Compares pairs of page images and generates an HTML to look at differences.
25*3ac0a46fSAndroid Build Coastguard Worker
26*3ac0a46fSAndroid Build Coastguard Worker  The images are all assumed to have the same name and be in two directories:
27*3ac0a46fSAndroid Build Coastguard Worker  [output_path]/[two_labels[0]] and [output_path]/[two_labels[1]]. For example,
28*3ac0a46fSAndroid Build Coastguard Worker  if output_path is "/tmp/images" and two_labels is ("before", "after"),
29*3ac0a46fSAndroid Build Coastguard Worker  images in /tmp/images/before will be compared to /tmp/images/after. The HTML
30*3ac0a46fSAndroid Build Coastguard Worker  produced will be in /tmp/images/compare.html and have relative links to these
31*3ac0a46fSAndroid Build Coastguard Worker  images, so /tmp/images is self-contained and can be moved around or shared.
32*3ac0a46fSAndroid Build Coastguard Worker  """
33*3ac0a46fSAndroid Build Coastguard Worker
34*3ac0a46fSAndroid Build Coastguard Worker  def __init__(self, build_dir, output_path, two_labels, num_workers,
35*3ac0a46fSAndroid Build Coastguard Worker               threshold_fraction):
36*3ac0a46fSAndroid Build Coastguard Worker    """Constructor.
37*3ac0a46fSAndroid Build Coastguard Worker
38*3ac0a46fSAndroid Build Coastguard Worker    Args:
39*3ac0a46fSAndroid Build Coastguard Worker      build_dir: Path to the build directory.
40*3ac0a46fSAndroid Build Coastguard Worker      output_path: Path with the pngs and where the html will be created.
41*3ac0a46fSAndroid Build Coastguard Worker      two_labels: Tuple of two strings that name the subdirectories in
42*3ac0a46fSAndroid Build Coastguard Worker          output_path containing the images.
43*3ac0a46fSAndroid Build Coastguard Worker      num_workers: Number of worker threads to start.
44*3ac0a46fSAndroid Build Coastguard Worker      threshold_fraction: Minimum percentage (0.0 to 1.0) of pixels below which
45*3ac0a46fSAndroid Build Coastguard Worker          an image is considered to have only small changes. They will not be
46*3ac0a46fSAndroid Build Coastguard Worker          displayed on the HTML, only listed.
47*3ac0a46fSAndroid Build Coastguard Worker    """
48*3ac0a46fSAndroid Build Coastguard Worker    self.build_dir = build_dir
49*3ac0a46fSAndroid Build Coastguard Worker    self.output_path = output_path
50*3ac0a46fSAndroid Build Coastguard Worker    self.two_labels = two_labels
51*3ac0a46fSAndroid Build Coastguard Worker    self.num_workers = num_workers
52*3ac0a46fSAndroid Build Coastguard Worker    self.threshold = threshold_fraction * 100
53*3ac0a46fSAndroid Build Coastguard Worker
54*3ac0a46fSAndroid Build Coastguard Worker  def Run(self, open_in_browser):
55*3ac0a46fSAndroid Build Coastguard Worker    """Runs the comparison and generates an HTML with the results.
56*3ac0a46fSAndroid Build Coastguard Worker
57*3ac0a46fSAndroid Build Coastguard Worker    Returns:
58*3ac0a46fSAndroid Build Coastguard Worker        Exit status.
59*3ac0a46fSAndroid Build Coastguard Worker    """
60*3ac0a46fSAndroid Build Coastguard Worker
61*3ac0a46fSAndroid Build Coastguard Worker    # Running a test defines a number of attributes on the fly.
62*3ac0a46fSAndroid Build Coastguard Worker    # pylint: disable=attribute-defined-outside-init
63*3ac0a46fSAndroid Build Coastguard Worker
64*3ac0a46fSAndroid Build Coastguard Worker    if len(self.two_labels) != 2:
65*3ac0a46fSAndroid Build Coastguard Worker      print('two_labels must be a tuple of length 2', file=sys.stderr)
66*3ac0a46fSAndroid Build Coastguard Worker      return 1
67*3ac0a46fSAndroid Build Coastguard Worker
68*3ac0a46fSAndroid Build Coastguard Worker    finder = DirectoryFinder(self.build_dir)
69*3ac0a46fSAndroid Build Coastguard Worker    self.img_diff_bin = finder.ExecutablePath('pdfium_diff')
70*3ac0a46fSAndroid Build Coastguard Worker
71*3ac0a46fSAndroid Build Coastguard Worker    html_path = os.path.join(self.output_path, 'compare.html')
72*3ac0a46fSAndroid Build Coastguard Worker
73*3ac0a46fSAndroid Build Coastguard Worker    self.diff_path = os.path.join(self.output_path, 'diff')
74*3ac0a46fSAndroid Build Coastguard Worker    if not os.path.exists(self.diff_path):
75*3ac0a46fSAndroid Build Coastguard Worker      os.makedirs(self.diff_path)
76*3ac0a46fSAndroid Build Coastguard Worker
77*3ac0a46fSAndroid Build Coastguard Worker    self.image_locations = ImageLocations(self.output_path, self.diff_path,
78*3ac0a46fSAndroid Build Coastguard Worker                                          self.two_labels)
79*3ac0a46fSAndroid Build Coastguard Worker
80*3ac0a46fSAndroid Build Coastguard Worker    difference = self._GenerateDiffs()
81*3ac0a46fSAndroid Build Coastguard Worker
82*3ac0a46fSAndroid Build Coastguard Worker    small_changes = []
83*3ac0a46fSAndroid Build Coastguard Worker
84*3ac0a46fSAndroid Build Coastguard Worker    with open(html_path, 'w') as f:
85*3ac0a46fSAndroid Build Coastguard Worker      f.write('<html><body>')
86*3ac0a46fSAndroid Build Coastguard Worker      f.write('<table>')
87*3ac0a46fSAndroid Build Coastguard Worker      for image in self.image_locations.Images():
88*3ac0a46fSAndroid Build Coastguard Worker        diff = difference[image]
89*3ac0a46fSAndroid Build Coastguard Worker        if diff is None:
90*3ac0a46fSAndroid Build Coastguard Worker          print('Failed to compare image %s' % image, file=sys.stderr)
91*3ac0a46fSAndroid Build Coastguard Worker        elif diff > self.threshold:
92*3ac0a46fSAndroid Build Coastguard Worker          self._WriteImageRows(f, image, diff)
93*3ac0a46fSAndroid Build Coastguard Worker        else:
94*3ac0a46fSAndroid Build Coastguard Worker          small_changes.append((image, diff))
95*3ac0a46fSAndroid Build Coastguard Worker      self._WriteSmallChanges(f, small_changes)
96*3ac0a46fSAndroid Build Coastguard Worker      f.write('</table>')
97*3ac0a46fSAndroid Build Coastguard Worker      f.write('</body></html>')
98*3ac0a46fSAndroid Build Coastguard Worker
99*3ac0a46fSAndroid Build Coastguard Worker    if open_in_browser:
100*3ac0a46fSAndroid Build Coastguard Worker      webbrowser.open(html_path)
101*3ac0a46fSAndroid Build Coastguard Worker
102*3ac0a46fSAndroid Build Coastguard Worker    return 0
103*3ac0a46fSAndroid Build Coastguard Worker
104*3ac0a46fSAndroid Build Coastguard Worker  def _GenerateDiffs(self):
105*3ac0a46fSAndroid Build Coastguard Worker    """Runs a diff over all pairs of page images, producing diff images.
106*3ac0a46fSAndroid Build Coastguard Worker
107*3ac0a46fSAndroid Build Coastguard Worker    As a side effect, the diff images will be saved to [output_path]/diff
108*3ac0a46fSAndroid Build Coastguard Worker    with the same image name.
109*3ac0a46fSAndroid Build Coastguard Worker
110*3ac0a46fSAndroid Build Coastguard Worker    Returns:
111*3ac0a46fSAndroid Build Coastguard Worker      A dict mapping image names to percentage of pixels changes.
112*3ac0a46fSAndroid Build Coastguard Worker    """
113*3ac0a46fSAndroid Build Coastguard Worker    difference = {}
114*3ac0a46fSAndroid Build Coastguard Worker    pool = multiprocessing.Pool(self.num_workers)
115*3ac0a46fSAndroid Build Coastguard Worker    worker_func = functools.partial(GenerateOneDiffParallel, self)
116*3ac0a46fSAndroid Build Coastguard Worker
117*3ac0a46fSAndroid Build Coastguard Worker    try:
118*3ac0a46fSAndroid Build Coastguard Worker      # The timeout is a workaround for http://bugs.python.org/issue8296
119*3ac0a46fSAndroid Build Coastguard Worker      # which prevents KeyboardInterrupt from working.
120*3ac0a46fSAndroid Build Coastguard Worker      one_year_in_seconds = 3600 * 24 * 365
121*3ac0a46fSAndroid Build Coastguard Worker      worker_results = (
122*3ac0a46fSAndroid Build Coastguard Worker          pool.map_async(
123*3ac0a46fSAndroid Build Coastguard Worker              worker_func,
124*3ac0a46fSAndroid Build Coastguard Worker              self.image_locations.Images()).get(one_year_in_seconds))
125*3ac0a46fSAndroid Build Coastguard Worker      for worker_result in worker_results:
126*3ac0a46fSAndroid Build Coastguard Worker        image, result = worker_result
127*3ac0a46fSAndroid Build Coastguard Worker        difference[image] = result
128*3ac0a46fSAndroid Build Coastguard Worker    except KeyboardInterrupt:
129*3ac0a46fSAndroid Build Coastguard Worker      pool.terminate()
130*3ac0a46fSAndroid Build Coastguard Worker      sys.exit(1)
131*3ac0a46fSAndroid Build Coastguard Worker    else:
132*3ac0a46fSAndroid Build Coastguard Worker      pool.close()
133*3ac0a46fSAndroid Build Coastguard Worker
134*3ac0a46fSAndroid Build Coastguard Worker    pool.join()
135*3ac0a46fSAndroid Build Coastguard Worker
136*3ac0a46fSAndroid Build Coastguard Worker    return difference
137*3ac0a46fSAndroid Build Coastguard Worker
138*3ac0a46fSAndroid Build Coastguard Worker  def GenerateOneDiff(self, image):
139*3ac0a46fSAndroid Build Coastguard Worker    """Runs a diff over one pair of images, producing a diff image.
140*3ac0a46fSAndroid Build Coastguard Worker
141*3ac0a46fSAndroid Build Coastguard Worker    As a side effect, the diff image will be saved to [output_path]/diff
142*3ac0a46fSAndroid Build Coastguard Worker    with the same image name.
143*3ac0a46fSAndroid Build Coastguard Worker
144*3ac0a46fSAndroid Build Coastguard Worker    Args:
145*3ac0a46fSAndroid Build Coastguard Worker      image: Page image to compare.
146*3ac0a46fSAndroid Build Coastguard Worker
147*3ac0a46fSAndroid Build Coastguard Worker    Returns:
148*3ac0a46fSAndroid Build Coastguard Worker      A tuple (image, diff), where image is the parameter and diff is the
149*3ac0a46fSAndroid Build Coastguard Worker      percentage of pixels changed.
150*3ac0a46fSAndroid Build Coastguard Worker    """
151*3ac0a46fSAndroid Build Coastguard Worker    try:
152*3ac0a46fSAndroid Build Coastguard Worker      subprocess.check_output([
153*3ac0a46fSAndroid Build Coastguard Worker          self.img_diff_bin,
154*3ac0a46fSAndroid Build Coastguard Worker          self.image_locations.Left(image),
155*3ac0a46fSAndroid Build Coastguard Worker          self.image_locations.Right(image)
156*3ac0a46fSAndroid Build Coastguard Worker      ])
157*3ac0a46fSAndroid Build Coastguard Worker    except subprocess.CalledProcessError as e:
158*3ac0a46fSAndroid Build Coastguard Worker      percentage_change = float(re.findall(r'\d+\.\d+', e.output)[0])
159*3ac0a46fSAndroid Build Coastguard Worker    else:
160*3ac0a46fSAndroid Build Coastguard Worker      return image, 0
161*3ac0a46fSAndroid Build Coastguard Worker
162*3ac0a46fSAndroid Build Coastguard Worker    try:
163*3ac0a46fSAndroid Build Coastguard Worker      subprocess.check_output([
164*3ac0a46fSAndroid Build Coastguard Worker          self.img_diff_bin, '--diff',
165*3ac0a46fSAndroid Build Coastguard Worker          self.image_locations.Left(image),
166*3ac0a46fSAndroid Build Coastguard Worker          self.image_locations.Right(image),
167*3ac0a46fSAndroid Build Coastguard Worker          self.image_locations.Diff(image)
168*3ac0a46fSAndroid Build Coastguard Worker      ])
169*3ac0a46fSAndroid Build Coastguard Worker    except subprocess.CalledProcessError as e:
170*3ac0a46fSAndroid Build Coastguard Worker      return image, percentage_change
171*3ac0a46fSAndroid Build Coastguard Worker    else:
172*3ac0a46fSAndroid Build Coastguard Worker      print('Warning: Should have failed the previous diff.', file=sys.stderr)
173*3ac0a46fSAndroid Build Coastguard Worker      return image, 0
174*3ac0a46fSAndroid Build Coastguard Worker
175*3ac0a46fSAndroid Build Coastguard Worker  def _GetRelativePath(self, absolute_path):
176*3ac0a46fSAndroid Build Coastguard Worker    return os.path.relpath(absolute_path, start=self.output_path)
177*3ac0a46fSAndroid Build Coastguard Worker
178*3ac0a46fSAndroid Build Coastguard Worker  def _WriteImageRows(self, f, image, diff):
179*3ac0a46fSAndroid Build Coastguard Worker    """Write table rows for a page image comparing its two versions.
180*3ac0a46fSAndroid Build Coastguard Worker
181*3ac0a46fSAndroid Build Coastguard Worker    Args:
182*3ac0a46fSAndroid Build Coastguard Worker      f: Open HTML file to write to.
183*3ac0a46fSAndroid Build Coastguard Worker      image: Image file name.
184*3ac0a46fSAndroid Build Coastguard Worker      diff: Percentage of different pixels.
185*3ac0a46fSAndroid Build Coastguard Worker    """
186*3ac0a46fSAndroid Build Coastguard Worker    f.write('<tr><td colspan="2">')
187*3ac0a46fSAndroid Build Coastguard Worker    f.write('%s (%.4f%% changed)' % (image, diff))
188*3ac0a46fSAndroid Build Coastguard Worker    f.write('</td></tr>')
189*3ac0a46fSAndroid Build Coastguard Worker
190*3ac0a46fSAndroid Build Coastguard Worker    f.write('<tr>')
191*3ac0a46fSAndroid Build Coastguard Worker    self._WritePageCompareTd(
192*3ac0a46fSAndroid Build Coastguard Worker        f, self._GetRelativePath(self.image_locations.Left(image)),
193*3ac0a46fSAndroid Build Coastguard Worker        self._GetRelativePath(self.image_locations.Right(image)))
194*3ac0a46fSAndroid Build Coastguard Worker    self._WritePageTd(f, self._GetRelativePath(
195*3ac0a46fSAndroid Build Coastguard Worker        self.image_locations.Diff(image)))
196*3ac0a46fSAndroid Build Coastguard Worker    f.write('</tr>')
197*3ac0a46fSAndroid Build Coastguard Worker
198*3ac0a46fSAndroid Build Coastguard Worker  def _WritePageTd(self, f, image_path):
199*3ac0a46fSAndroid Build Coastguard Worker    """Write table column with a single image.
200*3ac0a46fSAndroid Build Coastguard Worker
201*3ac0a46fSAndroid Build Coastguard Worker    Args:
202*3ac0a46fSAndroid Build Coastguard Worker      f: Open HTML file to write to.
203*3ac0a46fSAndroid Build Coastguard Worker      image_path: Path to image file.
204*3ac0a46fSAndroid Build Coastguard Worker    """
205*3ac0a46fSAndroid Build Coastguard Worker    f.write('<td>')
206*3ac0a46fSAndroid Build Coastguard Worker    f.write('<img src="%s">' % image_path)
207*3ac0a46fSAndroid Build Coastguard Worker    f.write('</td>')
208*3ac0a46fSAndroid Build Coastguard Worker
209*3ac0a46fSAndroid Build Coastguard Worker  def _WritePageCompareTd(self, f, normal_image_path, hover_image_path):
210*3ac0a46fSAndroid Build Coastguard Worker    """Write table column for an image comparing its two versions.
211*3ac0a46fSAndroid Build Coastguard Worker
212*3ac0a46fSAndroid Build Coastguard Worker    Args:
213*3ac0a46fSAndroid Build Coastguard Worker      f: Open HTML file to write to.
214*3ac0a46fSAndroid Build Coastguard Worker      normal_image_path: Path to image to be used in the "normal" state.
215*3ac0a46fSAndroid Build Coastguard Worker      hover_image_path: Path to image to be used in the "hover" state.
216*3ac0a46fSAndroid Build Coastguard Worker    """
217*3ac0a46fSAndroid Build Coastguard Worker    f.write('<td>')
218*3ac0a46fSAndroid Build Coastguard Worker    f.write('<img src="%s" '
219*3ac0a46fSAndroid Build Coastguard Worker            'onmouseover="this.src=\'%s\';" '
220*3ac0a46fSAndroid Build Coastguard Worker            'onmouseout="this.src=\'%s\';">' %
221*3ac0a46fSAndroid Build Coastguard Worker            (normal_image_path, hover_image_path, normal_image_path))
222*3ac0a46fSAndroid Build Coastguard Worker    f.write('</td>')
223*3ac0a46fSAndroid Build Coastguard Worker
224*3ac0a46fSAndroid Build Coastguard Worker  def _WriteSmallChanges(self, f, small_changes):
225*3ac0a46fSAndroid Build Coastguard Worker    """Write table rows for all images considered to have only small changes.
226*3ac0a46fSAndroid Build Coastguard Worker
227*3ac0a46fSAndroid Build Coastguard Worker    Args:
228*3ac0a46fSAndroid Build Coastguard Worker      f: Open HTML file to write to.
229*3ac0a46fSAndroid Build Coastguard Worker      small_changes: List of (image, change) tuples, where image is the page
230*3ac0a46fSAndroid Build Coastguard Worker          image and change is the percentage of pixels changed.
231*3ac0a46fSAndroid Build Coastguard Worker    """
232*3ac0a46fSAndroid Build Coastguard Worker    for image, change in small_changes:
233*3ac0a46fSAndroid Build Coastguard Worker      f.write('<tr><td colspan="2">')
234*3ac0a46fSAndroid Build Coastguard Worker      if not change:
235*3ac0a46fSAndroid Build Coastguard Worker        f.write('No change for: %s' % image)
236*3ac0a46fSAndroid Build Coastguard Worker      else:
237*3ac0a46fSAndroid Build Coastguard Worker        f.write('Small change of %.4f%% for: %s' % (change, image))
238*3ac0a46fSAndroid Build Coastguard Worker      f.write('</td></tr>')
239*3ac0a46fSAndroid Build Coastguard Worker
240*3ac0a46fSAndroid Build Coastguard Worker
241*3ac0a46fSAndroid Build Coastguard Workerclass ImageLocations:
242*3ac0a46fSAndroid Build Coastguard Worker  """Contains the locations of input and output image files.
243*3ac0a46fSAndroid Build Coastguard Worker  """
244*3ac0a46fSAndroid Build Coastguard Worker
245*3ac0a46fSAndroid Build Coastguard Worker  def __init__(self, output_path, diff_path, two_labels):
246*3ac0a46fSAndroid Build Coastguard Worker    """Constructor.
247*3ac0a46fSAndroid Build Coastguard Worker
248*3ac0a46fSAndroid Build Coastguard Worker    Args:
249*3ac0a46fSAndroid Build Coastguard Worker      output_path: Path to directory with the pngs.
250*3ac0a46fSAndroid Build Coastguard Worker      diff_path: Path to directory where the diffs will be generated.
251*3ac0a46fSAndroid Build Coastguard Worker      two_labels: Tuple of two strings that name the subdirectories in
252*3ac0a46fSAndroid Build Coastguard Worker          output_path containing the images.
253*3ac0a46fSAndroid Build Coastguard Worker    """
254*3ac0a46fSAndroid Build Coastguard Worker    self.output_path = output_path
255*3ac0a46fSAndroid Build Coastguard Worker    self.diff_path = diff_path
256*3ac0a46fSAndroid Build Coastguard Worker    self.two_labels = two_labels
257*3ac0a46fSAndroid Build Coastguard Worker
258*3ac0a46fSAndroid Build Coastguard Worker    self.left = self._FindImages(self.two_labels[0])
259*3ac0a46fSAndroid Build Coastguard Worker    self.right = self._FindImages(self.two_labels[1])
260*3ac0a46fSAndroid Build Coastguard Worker
261*3ac0a46fSAndroid Build Coastguard Worker    self.images = list(self.left.viewkeys() & self.right.viewkeys())
262*3ac0a46fSAndroid Build Coastguard Worker
263*3ac0a46fSAndroid Build Coastguard Worker    # Sort by pdf filename, then page number
264*3ac0a46fSAndroid Build Coastguard Worker    def KeyFn(s):
265*3ac0a46fSAndroid Build Coastguard Worker      pieces = s.rsplit('.', 2)
266*3ac0a46fSAndroid Build Coastguard Worker      return (pieces[0], int(pieces[1]))
267*3ac0a46fSAndroid Build Coastguard Worker
268*3ac0a46fSAndroid Build Coastguard Worker    self.images.sort(key=KeyFn)
269*3ac0a46fSAndroid Build Coastguard Worker    self.diff = {
270*3ac0a46fSAndroid Build Coastguard Worker        image: os.path.join(self.diff_path, image) for image in self.images
271*3ac0a46fSAndroid Build Coastguard Worker    }
272*3ac0a46fSAndroid Build Coastguard Worker
273*3ac0a46fSAndroid Build Coastguard Worker  def _FindImages(self, label):
274*3ac0a46fSAndroid Build Coastguard Worker    """Traverses a dir and builds a dict of all page images to compare in it.
275*3ac0a46fSAndroid Build Coastguard Worker
276*3ac0a46fSAndroid Build Coastguard Worker    Args:
277*3ac0a46fSAndroid Build Coastguard Worker      label: name of subdirectory of output_path to traverse.
278*3ac0a46fSAndroid Build Coastguard Worker
279*3ac0a46fSAndroid Build Coastguard Worker    Returns:
280*3ac0a46fSAndroid Build Coastguard Worker      Dict mapping page image names to the path of the image file.
281*3ac0a46fSAndroid Build Coastguard Worker    """
282*3ac0a46fSAndroid Build Coastguard Worker    image_path_matcher = os.path.join(self.output_path, label, '*.*.png')
283*3ac0a46fSAndroid Build Coastguard Worker    image_paths = glob.glob(image_path_matcher)
284*3ac0a46fSAndroid Build Coastguard Worker
285*3ac0a46fSAndroid Build Coastguard Worker    image_dict = {
286*3ac0a46fSAndroid Build Coastguard Worker        os.path.split(image_path)[1]: image_path for image_path in image_paths
287*3ac0a46fSAndroid Build Coastguard Worker    }
288*3ac0a46fSAndroid Build Coastguard Worker
289*3ac0a46fSAndroid Build Coastguard Worker    return image_dict
290*3ac0a46fSAndroid Build Coastguard Worker
291*3ac0a46fSAndroid Build Coastguard Worker  def Images(self):
292*3ac0a46fSAndroid Build Coastguard Worker    """Returns a list of all page images present in both directories."""
293*3ac0a46fSAndroid Build Coastguard Worker    return self.images
294*3ac0a46fSAndroid Build Coastguard Worker
295*3ac0a46fSAndroid Build Coastguard Worker  def Left(self, test_case):
296*3ac0a46fSAndroid Build Coastguard Worker    """Returns the path for a page image in the first subdirectory."""
297*3ac0a46fSAndroid Build Coastguard Worker    return self.left[test_case]
298*3ac0a46fSAndroid Build Coastguard Worker
299*3ac0a46fSAndroid Build Coastguard Worker  def Right(self, test_case):
300*3ac0a46fSAndroid Build Coastguard Worker    """Returns the path for a page image in the second subdirectory."""
301*3ac0a46fSAndroid Build Coastguard Worker    return self.right[test_case]
302*3ac0a46fSAndroid Build Coastguard Worker
303*3ac0a46fSAndroid Build Coastguard Worker  def Diff(self, test_case):
304*3ac0a46fSAndroid Build Coastguard Worker    """Returns the path for a page diff image."""
305*3ac0a46fSAndroid Build Coastguard Worker    return self.diff[test_case]
306