裁剪numpy.ndarray触发ValueError:无法保存空图像为JPEG
问题描述
尝试将numpy数组格式的图像裁剪为更小的图块并保存,但保存图块时出现“无法将空图像保存为JPEG”的错误。脚本已考虑无法凑齐图块尺寸的多余像素,并将其分配到图像边界,同时不确定图像的depth维度是否对应RGB通道及如何使用。
代码
print("Slicing Image.") path = 'Sliced/' filename= 'sample_svs_2_' img = tif.imread('sample_svs_2.svs') print(type(img)) print(img.shape) #This works, proving that #this method of saving is valid data = im.fromarray(img) data.save('Sliced/sample.jpg') #depth, height, width = img.shape height, width, depth = img.shape tilesize = 256 print("Height: ", height) print("Width: ", width) print("Depth: ", depth) leftExcessPixels = int((width%tilesize)/2) topExcessPixels = int((height%tilesize)/2) XNumberOfTiles = int(width/tilesize) YNumberOfTiles = int(height/tilesize) print('Left Excess Pixels: ' + str(leftExcessPixels) ) print('Top Excess Pixels: ' + str(topExcessPixels) ) print('Number of X tiles: ' + str(XNumberOfTiles)) print('Number of Y tiles: ' + str(YNumberOfTiles)) for y in range(YNumberOfTiles): for x in range(XNumberOfTiles): XStart = (leftExcessPixels + (tilesize * x)) YStart = (topExcessPixels + (tilesize * y)) XEnd = XStart + tilesize YEnd = YStart + tilesize croppedImage = img[XStart:YStart, XEnd:YEnd] data = im.fromarray(croppedImage) data.save('Sliced/sample_x' + str(x) + '_y' + str(y) + '.jpg')
终端输出
Slicing Image. <class 'numpy.ndarray'> (13271, 19992, 3) Height: 13271 Width: 19992 Depth: 3 Left Excess Pixels: 12 Top Excess Pixels: 107 Number of X tiles: 78 Number of Y tiles: 51 Traceback (most recent call last): File "c:\fileImageSlicer.py", line 72, in <module> sliceImage() File "c:\fileImageSlicer.py", line 52, in sliceImage data.save('Sliced/sample_x' + str(x) + '_y' + str(y) + '.jpg') ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\miniconda3\Lib\site-packages\PIL\Image.py", line 2439, in save save_handler(self, fp, filename) File "C:\miniconda3\Lib\site-packages\PIL\JpegImagePlugin.py", line 647, in _save raise ValueError(msg) ValueError: cannot write empty image as JPEG
问题分析与解决
核心错误:numpy数组切片顺序颠倒
从img.shape输出(13271, 19992, 3)可知,数组维度顺序是**(高度, 宽度, 通道数)**。但原代码的切片img[XStart:YStart, XEnd:YEnd]把宽度方向的索引放到了高度维度的位置,且XStart(宽度起始)远大于YStart(高度起始),导致切片范围起始索引大于结束索引,最终得到空数组,触发“无法保存空图像”的错误。关于depth维度的说明
这里的depth=3就是RGB三个颜色通道,切片时需要保留这个维度,否则生成的图块会丢失颜色信息,甚至因为维度不匹配导致PIL无法识别。
修正后的代码
仅需修改切片行,同时保留通道维度:
for y in range(YNumberOfTiles): for x in range(XNumberOfTiles): XStart = (leftExcessPixels + (tilesize * x)) YStart = (topExcessPixels + (tilesize * y)) XEnd = XStart + tilesize YEnd = YStart + tilesize croppedImage = img[YStart:YEnd, XStart:XEnd, :] # 修正切片顺序并保留所有通道 data = im.fromarray(croppedImage) data.save('Sliced/sample_x' + str(x) + '_y' + str(y) + '.jpg')
额外说明
- 切片时
:表示保留该维度的所有元素,这里就是保留RGB三个通道,确保生成的图块是彩色图像。 - 可在循环中添加
print(croppedImage.shape)验证切片正确性,正常输出应为(256, 256, 3)。
内容的提问来源于stack exchange,提问作者DrakeJest
相关产品推荐
相关产品推荐

