Pyinstaller .exe 找不到 _tiffile 模块 - 加载一些压缩图像会很慢
Pyinstaller .exe cannot find _tiffile module - Loading of some compressed images will be very slow
当我 运行 来自 Pyinstaller 的代码时,tiff reader 工作正常。使用 Pyinstaller 冻结后,我收到以下警告:
UserWarning: ImportError: No module named '_tifffile'. Loading of some compressed images will be very slow. Tifffile.c can be obtained at http://www.lfd.uci.edu/~gohlke
果然,过去需要几秒钟才能加载到 numpy 数组中的 tiff 文件现在可能需要几分钟。
这里是我的代码的简化形式,用于关注问题。如果您加载一个像 this one 这样的示例 tiff,它应该可以快速加载而不会出现问题。
如果您使用 C:\Python35\python.exe C:\Python35\Scripts\pyinstaller.exe --additional-hooks-dir=. --clean --win-private-assemblies tiffile_problems.py
,当您 运行 它时,您应该会得到一个带有上述错误消息的功能性 .exe。当您尝试加载相同的 tiff 时,它现在需要更长的时间。
tiffile_problems.py
#!/usr/bin/env python3
import os
import sys
import traceback
import numpy as np
import matplotlib.pyplot as plt
from PyQt4.QtGui import *
from PyQt4.QtCore import *
sys.path.append('..')
from MBE_for_SO.util import fileloader, fileconverter
class NotConvertedError(Exception):
pass
class FileAlreadyInProjectError(Exception):
def __init__(self, filename):
self.filename = filename
class Widget(QWidget):
def __init__(self, project, parent=None):
super(Widget, self).__init__(parent)
if not project:
self.setup_ui()
return
def setup_ui(self):
vbox = QVBoxLayout()
## Related to importing Raws
self.setWindowTitle('Import Raw File')
#vbox.addWidget(QLabel('Set the size all data are to be rescaled to'))
grid = QGridLayout()
vbox.addLayout(grid)
vbox.addStretch()
self.setLayout(vbox)
self.resize(400, 220)
self.listview = QListView()
self.listview.setStyleSheet('QListView::item { height: 26px; }')
self.listview.setSelectionMode(QAbstractItemView.NoSelection)
vbox.addWidget(self.listview)
hbox = QVBoxLayout()
pb = QPushButton('New Video')
pb.clicked.connect(self.new_video)
hbox.addWidget(pb)
vbox.addLayout(hbox)
vbox.addStretch()
self.setLayout(vbox)
def convert_tif(self, filename):
path = os.path.splitext(os.path.basename(filename))[0] + '.npy'
#path = os.path.join(self.project.path, path)
progress = QProgressDialog('Converting tif to npy...', 'Abort', 0, 100, self)
progress.setAutoClose(True)
progress.setMinimumDuration(0)
progress.setValue(0)
def callback(value):
progress.setValue(int(value * 100))
QApplication.processEvents()
try:
fileconverter.tif2npy(filename, path, callback)
print('Tifffile saved to wherever this script is')
except:
# qtutil.critical('Converting tiff to npy failed.')
progress.close()
return path
def to_npy(self, filename):
if filename.endswith('.raw'):
print('No raws allowed')
#filename = self.convert_raw(filename)
elif filename.endswith('.tif'):
filename = self.convert_tif(filename)
else:
raise fileloader.UnknownFileFormatError()
return filename
def import_file(self, filename):
if not filename.endswith('.npy'):
new_filename = self.to_npy(filename)
if not new_filename:
raise NotConvertedError()
else:
filename = new_filename
return filename
def import_files(self, filenames):
for filename in filenames:
try:
filename = self.import_file(filename)
except NotConvertedError:
# qtutil.warning('Skipping file \'{}\' since not converted.'.format(filename))
print('Skipping file \'{}\' since not converted.'.format(filename))
except FileAlreadyInProjectError as e:
# qtutil.warning('Skipping file \'{}\' since already in project.'.format(e.filename))
print('Skipping file \'{}\' since already in project.'.format(e.filename))
except:
# qtutil.critical('Import of \'{}\' failed:\n'.format(filename) +\
# traceback.format_exc())
print('Import of \'{}\' failed:\n'.format(filename) + traceback.format_exc())
# else:
# self.listview.model().appendRow(QStandardItem(filename))
def new_video(self):
filenames = QFileDialog.getOpenFileNames(
self, 'Load images', QSettings().value('last_load_data_path'),
'Video files (*.npy *.tif *.raw)')
if not filenames:
return
QSettings().setValue('last_load_data_path', os.path.dirname(filenames[0]))
self.import_files(filenames)
class MyPlugin:
def __init__(self, project):
self.name = 'Import video files'
self.widget = Widget(project)
def run(self):
pass
if __name__ == '__main__':
app = QApplication(sys.argv)
app.aboutToQuit.connect(app.deleteLater)
w = QMainWindow()
w.setCentralWidget(Widget(None))
w.show()
app.exec_()
sys.exit()
fileconverter.py
#!/usr/bin/env python3
import os
import numpy as np
import tifffile as tiff
class ConvertError(Exception):
pass
def tif2npy(filename_from, filename_to, progress_callback):
with tiff.TiffFile(filename_from) as tif:
w, h = tif[0].shape
shape = len(tif), w, h
np.save(filename_to, np.empty(shape, tif[0].dtype))
fp = np.load(filename_to, mmap_mode='r+')
for i, page in enumerate(tif):
progress_callback(i / float(shape[0]-1))
fp[i] = page.asarray()
def raw2npy(filename_from, filename_to, dtype, width, height,
num_channels, channel, progress_callback):
fp = np.memmap(filename_from, dtype, 'r')
frame_size = width * height * num_channels
if len(fp) % frame_size:
raise ConvertError()
num_frames = len(fp) / frame_size
fp = np.memmap(filename_from, dtype, 'r',
shape=(num_frames, width, height, num_channels))
np.save(filename_to, np.empty((num_frames, width, height), dtype))
fp_to = np.load(filename_to, mmap_mode='r+')
for i, frame in enumerate(fp):
progress_callback(i / float(len(fp)-1))
fp_to[i] = frame[:,:,channel-1]
fileloader.py
#!/usr/bin/env python3
import numpy as np
class UnknownFileFormatError(Exception):
pass
def load_npy(filename):
frames = np.load(filename)
# frames[np.isnan(frames)] = 0
return frames
def load_file(filename):
if filename.endswith('.npy'):
frames = load_npy(filename)
else:
raise UnknownFileFormatError()
return frames
def load_reference_frame_npy(filename, offset):
frames_mmap = np.load(filename, mmap_mode='c')
if frames_mmap is None:
return None
frame = np.array(frames_mmap[offset])
frame[np.isnan(frame)] = 0
frame = frame.swapaxes(0, 1)
if frame.ndim == 2:
frame = frame[:, ::-1]
elif frame.ndim == 3:
frame = frame[:, ::-1, :]
return frame
def load_reference_frame(filename, offset=0):
if filename.endswith('.npy'):
frame = load_reference_frame_npy(filename, offset)
else:
raise UnknownFileFormatError()
return frame
为什么?我该如何解决这个问题?我找到 tifffile.py, tifffile.cpython-35.pyc, tifffile.c
并将它们全部放在与 .exe 相同的目录中。没有效果。 _tifffile.cp35-win_amd64.pyd
由 pyinstaller 创建并放置在与 .exe 相同的目录中。我不知道我还有什么其他选择。
tifffile_problems.spec
# -*- mode: python -*-
block_cipher = None
a = Analysis(['tiffile_problems.py'],
pathex=['C:\Users\Cornelis\PycharmProjects\tester\MBE_for_SO'],
binaries=None,
datas=None,
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=True,
cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
exclude_binaries=True,
name='tiffile_problems',
debug=False,
strip=False,
upx=True,
console=True )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name='tiffile_problems')
tiffile.spec 当使用 C:\Python35\python.exe C:\Python35\Scripts\pyinstaller.exe --additional-hooks-dir=. --clean --win-private-assemblies --onefile tiffile_problems.py
# -*- mode: python -*-
block_cipher = None
a = Analysis(['tiffile_problems.py'],
pathex=['C:\Users\Cornelis\PycharmProjects\tester\MBE_for_SO'],
binaries=None,
datas=None,
hiddenimports=[],
hookspath=['.'],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=True,
cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
name='tiffile_problems',
debug=False,
strip=False,
upx=True,
console=True )
通过检查代码,tifffile.py
似乎正在寻找一个名为 _tifffile
的模块,这大概是编译后的 C 扩展的预期名称:
try:
if __package__:
from . import _tifffile
else:
import _tifffile
except ImportError:
warnings.warn(
"ImportError: No module named '_tifffile'. "
"Loading of some compressed images will be very slow. "
"Tifffile.c can be obtained at http://www.lfd.uci.edu/~gohlke/")
tifffile.cpython-35.pyc
就是tiffile.py
生成的字节码。你不需要为那个烦恼。
.c
文件本身也无济于事。它需要被编译以创建一个可用的 Python 扩展,它需要被命名为 _tiffile.cp35-win_amd64.pyd
(可能因您的系统和 python version/installation 而异)所以它可以被 import _tifffile
.
使用
如果您以前没有做过编译,那么编译可能是一项艰巨的任务。如果您觉得自己可以做到,Python documentation can help you get started. You will need to have the same Compiler 和您的 Python 版本编译时使用的设置。
但是,可能有更简单的解决方案。如果您的代码在冻结之前工作正常,那么您的系统上可能已经正确安装了编译的扩展。 Pyinstaller 可能会错过它,因为它可以找到 tifffile.py
并感到满意。在您的 Python 目录中查找正确的 .pyd
文件,看看您是否可以修改为您的项目创建的 .spec
file Pyinstaller,您在其中指定包含 .pyd
文件。
我实际上是在网上浏览时通过工作看到的,并决定玩一玩。
kazemakase 一开始就在正确的轨道上,当你 运行 正常时,__package__ 是 None,当它的打包 __package__ 设置为tifffile 并执行第一个条件,它成为相对于模块 tifffile.
if __package__:
from . import _tifffile
else:
import _tifffile
我只是手动将 tifffile 转换为模块,方法是在 site-packages 中创建一个 tifffile 文件夹,在新文件夹中创建一个空的 __init__.py 文件,放置 tifffile.py,_tifffile.pyd 从 site-packages 进入 tifffile 文件夹并更改 import 语句,诚然在一个简单的框架中。
import tifffile.tifffile as tiff
我不知道这是否对您的整个项目有帮助。并且应该注意,我最初使用 http://www.lfd.uci.edu/~gohlke/pythonlibs/ 中的轮子进行安装以节省编译步骤,因此您的里程可能会有所不同。我最初在 2.7 上执行了上述操作,但从我能够进行的一些测试来看,它似乎在 3.5 上也能正常工作。当我从最初生成的文件进行测试时,不需要在 .spec 文件中添加任何内容。
我认为 muggy 对 __package__
导致这里问题的怪异是正确的。我没有找到修复的确切原因,但这似乎已通过 pyinstaller
的最新更新得到解决。检查您的版本:
→ pyinstaller --version
3.2.1
并升级
→ pip3 install --upgrade pyinstaller
更新是在 2017 年 1 月 15 日才进行的,所以这对您最初提出的问题没有帮助,但现在确实有帮助。
安装:
->如果使用 conda,conda install tifffile -c conda-forge
-> 否则,pip install tifffile
当我 运行 来自 Pyinstaller 的代码时,tiff reader 工作正常。使用 Pyinstaller 冻结后,我收到以下警告:
UserWarning: ImportError: No module named '_tifffile'. Loading of some compressed images will be very slow. Tifffile.c can be obtained at http://www.lfd.uci.edu/~gohlke
果然,过去需要几秒钟才能加载到 numpy 数组中的 tiff 文件现在可能需要几分钟。
这里是我的代码的简化形式,用于关注问题。如果您加载一个像 this one 这样的示例 tiff,它应该可以快速加载而不会出现问题。
如果您使用 C:\Python35\python.exe C:\Python35\Scripts\pyinstaller.exe --additional-hooks-dir=. --clean --win-private-assemblies tiffile_problems.py
,当您 运行 它时,您应该会得到一个带有上述错误消息的功能性 .exe。当您尝试加载相同的 tiff 时,它现在需要更长的时间。
tiffile_problems.py
#!/usr/bin/env python3
import os
import sys
import traceback
import numpy as np
import matplotlib.pyplot as plt
from PyQt4.QtGui import *
from PyQt4.QtCore import *
sys.path.append('..')
from MBE_for_SO.util import fileloader, fileconverter
class NotConvertedError(Exception):
pass
class FileAlreadyInProjectError(Exception):
def __init__(self, filename):
self.filename = filename
class Widget(QWidget):
def __init__(self, project, parent=None):
super(Widget, self).__init__(parent)
if not project:
self.setup_ui()
return
def setup_ui(self):
vbox = QVBoxLayout()
## Related to importing Raws
self.setWindowTitle('Import Raw File')
#vbox.addWidget(QLabel('Set the size all data are to be rescaled to'))
grid = QGridLayout()
vbox.addLayout(grid)
vbox.addStretch()
self.setLayout(vbox)
self.resize(400, 220)
self.listview = QListView()
self.listview.setStyleSheet('QListView::item { height: 26px; }')
self.listview.setSelectionMode(QAbstractItemView.NoSelection)
vbox.addWidget(self.listview)
hbox = QVBoxLayout()
pb = QPushButton('New Video')
pb.clicked.connect(self.new_video)
hbox.addWidget(pb)
vbox.addLayout(hbox)
vbox.addStretch()
self.setLayout(vbox)
def convert_tif(self, filename):
path = os.path.splitext(os.path.basename(filename))[0] + '.npy'
#path = os.path.join(self.project.path, path)
progress = QProgressDialog('Converting tif to npy...', 'Abort', 0, 100, self)
progress.setAutoClose(True)
progress.setMinimumDuration(0)
progress.setValue(0)
def callback(value):
progress.setValue(int(value * 100))
QApplication.processEvents()
try:
fileconverter.tif2npy(filename, path, callback)
print('Tifffile saved to wherever this script is')
except:
# qtutil.critical('Converting tiff to npy failed.')
progress.close()
return path
def to_npy(self, filename):
if filename.endswith('.raw'):
print('No raws allowed')
#filename = self.convert_raw(filename)
elif filename.endswith('.tif'):
filename = self.convert_tif(filename)
else:
raise fileloader.UnknownFileFormatError()
return filename
def import_file(self, filename):
if not filename.endswith('.npy'):
new_filename = self.to_npy(filename)
if not new_filename:
raise NotConvertedError()
else:
filename = new_filename
return filename
def import_files(self, filenames):
for filename in filenames:
try:
filename = self.import_file(filename)
except NotConvertedError:
# qtutil.warning('Skipping file \'{}\' since not converted.'.format(filename))
print('Skipping file \'{}\' since not converted.'.format(filename))
except FileAlreadyInProjectError as e:
# qtutil.warning('Skipping file \'{}\' since already in project.'.format(e.filename))
print('Skipping file \'{}\' since already in project.'.format(e.filename))
except:
# qtutil.critical('Import of \'{}\' failed:\n'.format(filename) +\
# traceback.format_exc())
print('Import of \'{}\' failed:\n'.format(filename) + traceback.format_exc())
# else:
# self.listview.model().appendRow(QStandardItem(filename))
def new_video(self):
filenames = QFileDialog.getOpenFileNames(
self, 'Load images', QSettings().value('last_load_data_path'),
'Video files (*.npy *.tif *.raw)')
if not filenames:
return
QSettings().setValue('last_load_data_path', os.path.dirname(filenames[0]))
self.import_files(filenames)
class MyPlugin:
def __init__(self, project):
self.name = 'Import video files'
self.widget = Widget(project)
def run(self):
pass
if __name__ == '__main__':
app = QApplication(sys.argv)
app.aboutToQuit.connect(app.deleteLater)
w = QMainWindow()
w.setCentralWidget(Widget(None))
w.show()
app.exec_()
sys.exit()
fileconverter.py
#!/usr/bin/env python3
import os
import numpy as np
import tifffile as tiff
class ConvertError(Exception):
pass
def tif2npy(filename_from, filename_to, progress_callback):
with tiff.TiffFile(filename_from) as tif:
w, h = tif[0].shape
shape = len(tif), w, h
np.save(filename_to, np.empty(shape, tif[0].dtype))
fp = np.load(filename_to, mmap_mode='r+')
for i, page in enumerate(tif):
progress_callback(i / float(shape[0]-1))
fp[i] = page.asarray()
def raw2npy(filename_from, filename_to, dtype, width, height,
num_channels, channel, progress_callback):
fp = np.memmap(filename_from, dtype, 'r')
frame_size = width * height * num_channels
if len(fp) % frame_size:
raise ConvertError()
num_frames = len(fp) / frame_size
fp = np.memmap(filename_from, dtype, 'r',
shape=(num_frames, width, height, num_channels))
np.save(filename_to, np.empty((num_frames, width, height), dtype))
fp_to = np.load(filename_to, mmap_mode='r+')
for i, frame in enumerate(fp):
progress_callback(i / float(len(fp)-1))
fp_to[i] = frame[:,:,channel-1]
fileloader.py
#!/usr/bin/env python3
import numpy as np
class UnknownFileFormatError(Exception):
pass
def load_npy(filename):
frames = np.load(filename)
# frames[np.isnan(frames)] = 0
return frames
def load_file(filename):
if filename.endswith('.npy'):
frames = load_npy(filename)
else:
raise UnknownFileFormatError()
return frames
def load_reference_frame_npy(filename, offset):
frames_mmap = np.load(filename, mmap_mode='c')
if frames_mmap is None:
return None
frame = np.array(frames_mmap[offset])
frame[np.isnan(frame)] = 0
frame = frame.swapaxes(0, 1)
if frame.ndim == 2:
frame = frame[:, ::-1]
elif frame.ndim == 3:
frame = frame[:, ::-1, :]
return frame
def load_reference_frame(filename, offset=0):
if filename.endswith('.npy'):
frame = load_reference_frame_npy(filename, offset)
else:
raise UnknownFileFormatError()
return frame
为什么?我该如何解决这个问题?我找到 tifffile.py, tifffile.cpython-35.pyc, tifffile.c
并将它们全部放在与 .exe 相同的目录中。没有效果。 _tifffile.cp35-win_amd64.pyd
由 pyinstaller 创建并放置在与 .exe 相同的目录中。我不知道我还有什么其他选择。
tifffile_problems.spec
# -*- mode: python -*-
block_cipher = None
a = Analysis(['tiffile_problems.py'],
pathex=['C:\Users\Cornelis\PycharmProjects\tester\MBE_for_SO'],
binaries=None,
datas=None,
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=True,
cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
exclude_binaries=True,
name='tiffile_problems',
debug=False,
strip=False,
upx=True,
console=True )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name='tiffile_problems')
tiffile.spec 当使用 C:\Python35\python.exe C:\Python35\Scripts\pyinstaller.exe --additional-hooks-dir=. --clean --win-private-assemblies --onefile tiffile_problems.py
# -*- mode: python -*-
block_cipher = None
a = Analysis(['tiffile_problems.py'],
pathex=['C:\Users\Cornelis\PycharmProjects\tester\MBE_for_SO'],
binaries=None,
datas=None,
hiddenimports=[],
hookspath=['.'],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=True,
cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
name='tiffile_problems',
debug=False,
strip=False,
upx=True,
console=True )
通过检查代码,tifffile.py
似乎正在寻找一个名为 _tifffile
的模块,这大概是编译后的 C 扩展的预期名称:
try:
if __package__:
from . import _tifffile
else:
import _tifffile
except ImportError:
warnings.warn(
"ImportError: No module named '_tifffile'. "
"Loading of some compressed images will be very slow. "
"Tifffile.c can be obtained at http://www.lfd.uci.edu/~gohlke/")
tifffile.cpython-35.pyc
就是tiffile.py
生成的字节码。你不需要为那个烦恼。
.c
文件本身也无济于事。它需要被编译以创建一个可用的 Python 扩展,它需要被命名为 _tiffile.cp35-win_amd64.pyd
(可能因您的系统和 python version/installation 而异)所以它可以被 import _tifffile
.
如果您以前没有做过编译,那么编译可能是一项艰巨的任务。如果您觉得自己可以做到,Python documentation can help you get started. You will need to have the same Compiler 和您的 Python 版本编译时使用的设置。
但是,可能有更简单的解决方案。如果您的代码在冻结之前工作正常,那么您的系统上可能已经正确安装了编译的扩展。 Pyinstaller 可能会错过它,因为它可以找到 tifffile.py
并感到满意。在您的 Python 目录中查找正确的 .pyd
文件,看看您是否可以修改为您的项目创建的 .spec
file Pyinstaller,您在其中指定包含 .pyd
文件。
我实际上是在网上浏览时通过工作看到的,并决定玩一玩。
kazemakase 一开始就在正确的轨道上,当你 运行 正常时,__package__ 是 None,当它的打包 __package__ 设置为tifffile 并执行第一个条件,它成为相对于模块 tifffile.
if __package__:
from . import _tifffile
else:
import _tifffile
我只是手动将 tifffile 转换为模块,方法是在 site-packages 中创建一个 tifffile 文件夹,在新文件夹中创建一个空的 __init__.py 文件,放置 tifffile.py,_tifffile.pyd 从 site-packages 进入 tifffile 文件夹并更改 import 语句,诚然在一个简单的框架中。
import tifffile.tifffile as tiff
我不知道这是否对您的整个项目有帮助。并且应该注意,我最初使用 http://www.lfd.uci.edu/~gohlke/pythonlibs/ 中的轮子进行安装以节省编译步骤,因此您的里程可能会有所不同。我最初在 2.7 上执行了上述操作,但从我能够进行的一些测试来看,它似乎在 3.5 上也能正常工作。当我从最初生成的文件进行测试时,不需要在 .spec 文件中添加任何内容。
我认为 muggy 对 __package__
导致这里问题的怪异是正确的。我没有找到修复的确切原因,但这似乎已通过 pyinstaller
的最新更新得到解决。检查您的版本:
→ pyinstaller --version
3.2.1
并升级
→ pip3 install --upgrade pyinstaller
更新是在 2017 年 1 月 15 日才进行的,所以这对您最初提出的问题没有帮助,但现在确实有帮助。
安装:
->如果使用 conda,conda install tifffile -c conda-forge
-> 否则,pip install tifffile