diff --git a/sources/test/large_image_source_test/__init__.py b/sources/test/large_image_source_test/__init__.py index d890f23cf..198a5c124 100644 --- a/sources/test/large_image_source_test/__init__.py +++ b/sources/test/large_image_source_test/__init__.py @@ -25,7 +25,7 @@ from large_image.constants import TILE_FORMAT_NUMPY, TILE_FORMAT_PIL, SourcePriority from large_image.exceptions import TileSourceError from large_image.tilesource import TileSource -from large_image.tilesource.utilities import _imageToNumpy +from large_image.tilesource.utilities import _imageToNumpy, _imageToPIL try: from importlib.metadata import PackageNotFoundError @@ -178,37 +178,35 @@ def getInternalMetadata(self, **kwargs): """ return {'fractal': self.fractal, 'monochrome': self.monochrome} - @methodcache() - def getTile(self, x, y, z, *args, **kwargs): - frame = self._getFrame(**kwargs) - self._xyzInRange(x, y, z, frame, len(self._frames) if hasattr(self, '_frames') else None) - - if not (self.minLevel <= z <= self.maxLevel): - raise TileSourceError('z layer does not exist') - - xFraction = (x + 0.5) * self.tileWidth * 2 ** (self.levels - 1 - z) / self.sizeX - yFraction = (y + 0.5) * self.tileHeight * 2 ** (self.levels - 1 - z) / self.sizeY - fFraction = yFraction - if hasattr(self, '_frames'): - fFraction = float(frame) / (len(self._frames) - 1) - - backgroundColor = colorsys.hsv_to_rgb( - h=xFraction, - s=(0.3 + (0.7 * fFraction)), - v=(0.3 + (0.7 * yFraction)), - ) - rgbColor = tuple(int(val * 255) for val in backgroundColor) - + def _tileImage(self, rgbColor, x, y, z, frame, band=None, bandnum=0): image = Image.new( mode='RGB', size=(self.tileWidth, self.tileHeight), color=(rgbColor if not self.fractal else (255, 255, 255)) ) - imageDraw = ImageDraw.Draw(image) - if self.fractal: self.fractalTile(image, x, y, 2 ** z, rgbColor) + bandtext = '\n' if band is not None else '' + if bandnum and band and band.lower() not in { + 'r', 'red', 'g', 'green', 'b', 'blue', 'grey', 'gray', 'alpha'}: + bandtext += band + image = _imageToNumpy(image)[0].astype(float) + vstripe = numpy.array([ + int(x / (self.tileWidth / bandnum / 2)) % 2 + for x in range(self.tileWidth)]) + hstripe = numpy.array([ + int(y / (self.tileHeight / (bandnum % self.tileWidth) / 2)) % 2 + if bandnum > self.tileWidth else 1 for y in range(self.tileHeight)]) + simage = image.copy() + simage[hstripe == 0, :, :] /= 2 + simage[:, vstripe == 0, :] /= 2 + image = numpy.where(image != 255, simage, image) + image = image.astype(numpy.uint8) + image = _imageToPIL(image) + + imageDraw = ImageDraw.Draw(image) + fontsize = 0.15 text = 'x=%d\ny=%d\nz=%d' % (x, y, z) if hasattr(self, '_frames'): @@ -219,7 +217,8 @@ def getTile(self, x, y, z, *args, **kwargs): ('T', 'IndexT'), ('XY', 'IndexXY')]: if k2 in self._frames[frame]: text += '\n%s=%d' % (k1, self._frames[frame][k2]) - fontsize = min(fontsize, 0.8 / len(text.split('\n'))) + text += bandtext + fontsize = min(fontsize, 0.8 / len(text.split('\n'))) try: # the font size should fill the whole tile imageDrawFont = ImageFont.truetype( @@ -231,23 +230,49 @@ def getTile(self, x, y, z, *args, **kwargs): imageDraw.multiline_text( xy=(10, 10), text=text, - fill=(0, 0, 0), + fill=(0, 0, 0) if band != 'alpha' else (255, 255, 255), font=imageDrawFont ) + return image + + @methodcache() + def getTile(self, x, y, z, *args, **kwargs): + frame = self._getFrame(**kwargs) + self._xyzInRange(x, y, z, frame, len(self._frames) if hasattr(self, '_frames') else None) + + if not (self.minLevel <= z <= self.maxLevel): + raise TileSourceError('z layer does not exist') _counters['tiles'] += 1 - if self.monochrome: - image = image.convert('L') - if not self._bands or len(self._bands) == len(image.mode): - return self._outputTile(image, TILE_FORMAT_PIL, x, y, z, **kwargs) - image = _imageToNumpy(image)[0] - newimg = numpy.zeros((image.shape[0], image.shape[1], len(self._bands))) - newimg[:, :, :image.shape[2]] = image - for b in range(image.shape[2], len(self._bands)): - newimg[:, :, b] = (b / len(self._bands) + (1 - b / len(self._bands)) * xFraction) * 255 - newimg[image[:, :, 0] == 0] = 255 if self._bands[b] in {'alpha', 'A'} else 0 - newimg[image[:, :, 0] == 255] = 255 - image = newimg - return self._outputTile(image, TILE_FORMAT_NUMPY, x, y, z, **kwargs) + + xFraction = (x + 0.5) * self.tileWidth * 2 ** (self.levels - 1 - z) / self.sizeX + yFraction = (y + 0.5) * self.tileHeight * 2 ** (self.levels - 1 - z) / self.sizeY + fFraction = yFraction + if hasattr(self, '_frames'): + fFraction = float(frame) / (len(self._frames) - 1) + + backgroundColor = colorsys.hsv_to_rgb( + h=xFraction, + s=(0.3 + (0.7 * fFraction)), + v=(0.3 + (0.7 * yFraction)), + ) + rgbColor = tuple(int(val * 255) for val in backgroundColor) + + if not self._bands or len(self._bands) == (1 if self.monochrome else 3): + image = self._tileImage(rgbColor, x, y, z, frame) + if self.monochrome: + image = image.convert('L') + format = TILE_FORMAT_PIL + else: + image = numpy.zeros( + (self.tileHeight, self.tileWidth, len(self._bands)), dtype=numpy.uint8) + for bandnum, band in enumerate(self._bands): + bandimg = self._tileImage(rgbColor, x, y, z, frame, band, bandnum) + bandimg = _imageToNumpy(bandimg)[0] + if self.monochrome or band.upper() in {'grey', 'gray', 'alpha'}: + bandimg = bandimg.convert('L') + image[:, :, bandnum] = bandimg[:, :, bandnum % bandimg.shape[2]] + format = TILE_FORMAT_NUMPY + return self._outputTile(image, format, x, y, z, **kwargs) @staticmethod def getLRUHash(*args, **kwargs): diff --git a/test/test_files/multi_band.yml b/test/test_files/multi_band.yml index 8863922c9..fe249c983 100644 --- a/test/test_files/multi_band.yml +++ b/test/test_files/multi_band.yml @@ -11,7 +11,7 @@ sources: sizeY: 7500 fractal: True # c,z,t,xy OR frames - frames: "4,6,1,1" + frames: "4,5,1,1" monochrome: False # multiband - bands: "red,green,blue,ir1,ir2" + bands: "red,green,blue,ir1,ir2,grey" diff --git a/test/test_source_multi.py b/test/test_source_multi.py index 2ce5f5217..a71802047 100644 --- a/test/test_source_multi.py +++ b/test/test_source_multi.py @@ -153,6 +153,6 @@ def testMultiBand(): imagePath = os.path.join(testDir, 'test_files', 'multi_band.yml') source = large_image_source_multi.open(imagePath) metadata = source.getMetadata() - assert len(metadata['bands']) == 5 + assert len(metadata['bands']) == 6 image, mimeType = source.getThumbnail(encoding='PNG') assert image[:len(utilities.PNGHeader)] == utilities.PNGHeader