Pyinstaller .exe не может найти модуль _tiffile - загрузка некоторых сжатых изображений будет очень медленной

Когда я запускаю свой код из Pyinstaller, TIFF работает нормально. После замораживания с помощью 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

И, конечно же, файл TIFF, который раньше загружался в пустой массив, занимал секунды, теперь может занимать минуты.

Вот упрощенная форма моего кода, чтобы сосредоточиться на проблеме. Если вы загрузите пример 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 )

Ответы на вопрос(4)

Ваш ответ на вопрос