You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

裁剪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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 21:25:02