2016-10-27 2 views
5

Когда я запускаю свой код из Pyinstaller, tiff reader отлично работает. После замораживания с использованием Pyinstaller я получаю следующее предупреждение:Pyinstaller .exe не может найти модуль _tiffile. Загрузка некоторых сжатых изображений будет очень медленной.

enter image description here

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

Вот упрощенная форма моего кода, чтобы сосредоточиться на проблеме. Если вы загружаете пример tiff как this one, он должен быстро загружаться без проблем.

Если вы используете 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) 

ответ

1

Я думаю, что магия правильна о его странности с __package__, вызывающим проблему здесь. Я не нашел точную причину исправления, но это, похоже, устранено с использованием последнего обновления до pyinstaller. Проверьте версию с:

→ pyinstaller --version 3.2.1

и обновление с

→ pip3 install --upgrade pyinstaller

обновление было сделано только на 15 января 2017, так что это не помогло бы, когда вы первоначально просили, но теперь помогает.

0

От проверки кода, 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), поэтому его можно использовать import _tifffile.

Компиляция может быть сложной задачей, если вы этого еще не сделали. Если вы чувствуете, что делаете это, Python documentation может помочь вам приступить к работе. Вам понадобится same Compiler и параметры, с которыми была скомпилирована ваша версия Python.

Однако может быть более простое решение.Если ваш код работает отлично до замораживания, возможно, у вас есть скомпилированное расширение, правильно установленное в вашей системе. Pyinstaller, вероятно, пропускает его, потому что он может найти tifffile.py и удовлетворен. Найдите нужный файл .pyd в своих каталогах Python и посмотрите, можете ли вы изменить созданный для вашего проекта созданный для вашего проекта созданный для вашего проекта создатель 0install, который вы указываете для включения файла .pyd.

+0

У меня нет проблем с моим кодом перед замораживанием. Файлы Tiff загружаются красиво и быстро. _tifffile.cp35-win_amd64.pyd создается pyinstaller, и я могу найти его в каталоге, который содержит .exe. Я прочитал вашу ссылку, но я не уверен, как включить _tifffile.cp35-win_amd64.pyd в .spec. Я добавил свой .spec к вопросу – Frikster

+0

Я попытался поместить datas = [('_tifffile.cp35-win_amd64.pyd')] в качестве поля в спецификации в Analysis. Но когда я запускаю pyinstaler, я замечаю, что спецификация возвращается к тому, как это было. – Frikster

+0

ok nvm Мне удалось изменить спецификацию, чтобы включить файл .pyd. Но это не имеет никакого значения, как и следовало ожидать, поскольку файл уже помещен в каталог, который содержит .exe по умолчанию. Это, естественно, заставляет меня задаться вопросом, что происходит? Если файл .c уже скомпилирован, и он правильно там, почему он не импортируется с импортом _tifffile? – Frikster

1

Я действительно видел это через upwork, пока я просто просматривал сеть и решил поиграть.

kazemakase был на правильном пути в самом начале, когда вы запускаете нормально, __package__ - None, а когда его упакованный __package__ установлен в tifffile и выполняется первое условие, и оно становится относительно модуля tifffile.

if __package__: 
    from . import _tifffile 
else: 
    import _tifffile 

Я просто конвертируюсь tifffile в модуль вручную, путем создания в Зоне-пакетах папки tifffile, создавая пустой файл __init__.py в новой папке, размещая tifffile.py, _tifffile.pyd с сайтом-пакеты в папку tifffile и изменение оператора импорта, по общему признанию, в простом скелете.

import tifffile.tifffile as tiff 

Если это помогает в рамках всего вашего проекта, я не знаю. И следует отметить, что я использовал колесо от http://www.lfd.uci.edu/~gohlke/pythonlibs/ для первоначальной установки, чтобы сохранить шаг компиляции, чтобы ваш пробег мог отличаться. Сначала я сделал это на 2.7, но, похоже, он отлично работает на 3.5 из некоторых тестов, которые я смог сделать. И не нужно было помещать что-либо в файлы .spec, когда я тестировал их изначально созданных.

+0

Так что я хочу подтвердить ваш ответ. Однако я сначала попробовал простое решение aconz2, и оно сработало (также было удалено и повторно установлено tifffile с колесика колес). Теперь, когда я пытаюсь вернуться к предыдущей версии pyinstaller (3.2), я не могу реплицировать ошибку! Он просто исправлен и остается фиксированным! Очень странно. Это означает, что я не могу проверить, что ваш ответ работает, поскольку я бы предпочел не вникать в то, сделал ли я некоторые другие случайные изменения, так как я просто рад, что он работает сейчас. В любом случае, я дам вам +1, так как вы, вероятно, более прав, объясняя, откуда произошла ошибка, чем кто-либо другой. – Frikster

+0

Нет проблем. Я подозреваю, что вы не можете повторно протестировать, так как вы обновили setuptools/packaging и thats, где было исправлено основное исправление. – muggy

Смежные вопросы