2016-10-27 48 views
5

Po uruchomieniu mojego kodu z Pyinstallera czytnik tiff działa dobrze. Po zamrożeniu korzystając Pyinstaller uzyskać następujące ostrzeżenie:Plikoinstalator .exe nie może znaleźć modułu _tiffile - ładowanie niektórych skompresowanych obrazów będzie bardzo powolne

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 

I na pewno wystarczy, plik TIFF, że wykorzystywane do podejmowania sekund, aby załadować do tablicy numpy może teraz podjąć minut.

Oto uproszczona forma mojego kodu, aby skupić się na problemie. Jeśli załadujesz przykładowy plik tiff, taki jak this one, powinien on być ładowany szybko bez problemów.

Jeśli używasz C:\Python35\python.exe C:\Python35\Scripts\pyinstaller.exe --additional-hooks-dir=. --clean --win-private-assemblies tiffile_problems.py powinieneś uzyskać funkcjonalny plik .exe z powyższym komunikatem o błędzie po uruchomieniu. Kiedy próbujesz załadować ten sam tiff, teraz trwa to znacznie dłużej.

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 

Czemu? I jak to naprawić? Zlokalizowałem tifffile.py, tifffile.cpython-35.pyc, tifffile.c i umieściłem je wszystkie w tym samym katalogu, co .exe. Bez efektu. _tifffile.cp35-win_amd64.pyd jest tworzony przez Pyinstaller i umieszczany w tym samym katalogu, co .exe. Nie wiem, jakie inne opcje są dla mnie dostępne.

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 podczas korzystania 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) 

Odpowiedz

1

Myślę, że parszywy ma rację co do dziwności z __package__, powodując problem tutaj. Nie znalazłem dokładnej przyczyny tej poprawki, ale wydaje się, że problem został rozwiązany przy użyciu najnowszej aktualizacji do pyinstaller. Sprawdź swoją wersję z:

→ pyinstaller --version 3.2.1

i uaktualnienia z

→ pip3 install --upgrade pyinstaller

Aktualizacja została wykonana dopiero w dniu 15 stycznia 2017, więc to nie pomogło, kiedy pierwotnie zapytał, ale z pewnością pomaga teraz.

0

Od sprawdzania kodu tifffile.py wydaje się szuka modułu zwanego _tifffile, co przypuszczalnie jest oczekiwana nazwa skompilowanego rozszerzenia 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/") 

Numer tifffile.cpython-35.pyc to kod bajtowy wygenerowany z tiffile.py. Nie musisz się tym martwić.

Sam plik też nie pomoże. Musi zostać skompilowany, aby utworzyć użyteczne rozszerzenie Pythona, które musi być nazywane coś w rodzaju _tiffile.cp35-win_amd64.pyd (może się różnić w zależności od systemu i wersji pythona/instalacji), więc może być używane przez import _tifffile.

Kompilowanie może być trudnym zadaniem, jeśli jeszcze tego nie zrobiłeś. Jeśli czujesz, że do niego pasujesz, pomocna może być Python documentation. Będziesz musiał mieć same Compiler i ustawienia, z którymi została skompilowana twoja wersja Pythona.

Jednak może istnieć prostsze rozwiązanie.Jeśli twój kod działa poprawnie przed zamrożeniem, prawdopodobnie masz zainstalowane poprawnie skompilowane rozszerzenie w twoim systemie. Pyinstaller prawdopodobnie tęskni za nią, ponieważ może znaleźć tifffile.py i jest zadowolony. Wyszukaj poprawny plik .pyd w swoich katalogach Pythona i sprawdź, czy można zmodyfikować Pyinstaller .spec file utworzony dla projektu, w którym należy podać plik .pyd.

+0

Nie mam żadnych problemów z moim kodem przed zamrożeniem. Pliki Tiff ładują się ładnie i szybko. _tifffile.cp35-win_amd64.pyd jest tworzony przez Pyinstaller i mogę go znaleźć w katalogu, który zawiera .exe. Przeczytałem twój link, ale nie jestem pewien, jak dołączyć _tifffile.cp35-win_amd64.pyd do .spec. Dodałem moje .spec do pytania – Frikster

+0

Próbowałem umieścić datas = [('_tifffile.cp35-win_amd64.pyd ")] jako pole w specyfikacji w Analizie. Ale kiedy uruchamiam program antywirusowy, zauważam, że specyfikacja powraca do stanu wyjściowego. – Frikster

+0

ok nvm Udało mi się zmodyfikować specyfikację, aby dołączyć plik .pyd. Ale nie robi to żadnej różnicy, jak powinienem się spodziewać, ponieważ plik jest już umieszczony w katalogu zawierającym domyślnie .exe. To naturalnie sprawia, że ​​zastanawiam się, co się dzieje? Jeśli plik .c jest już skompilowany i właśnie tam jest, dlaczego nie jest importowany z importem _tifffile? – Frikster

1

Rzeczywiście widziałem to przez pracę podczas gdy przeglądałem sieć i zdecydowałem się na zabawę.

kazemakase był na właściwym torze na samym początku, kiedy normalnie biegniesz, __package__ jest Brak, a gdy jego pakowany __package__ jest ustawiony na tifffile i pierwszy warunek jest wykonywany, i staje się względny względem tifffile modułu.

if __package__: 
    from . import _tifffile 
else: 
    import _tifffile 

właśnie przekształca tifffile do modułu ręcznie, tworząc w miejscu opakowań folder tifffile, tworząc pustą plik __init__.py w nowym folderze umieszczenie tifffile.py, _tifffile.pyd z zakładu opakowań do folderu tifffile i zmieniając instrukcję importu, wprawdzie w prostym szkielecie.

import tifffile.tifffile as tiff 

Jeśli to pomaga w całym projekcie, nie wiem. I należy pamiętać, że użyłem koła od http://www.lfd.uci.edu/~gohlke/pythonlibs/, aby zainstalować pierwotnie, aby zapisać krok kompilacji, tak aby przebieg może się różnić. Zrobiłem powyższe początkowo na 2.7, ale wydaje mi się również, że działa dobrze na 3.5 z niektórych testów, które udało mi się wykonać. Nie musiałem też niczego umieszczać w plikach .spec, kiedy testowałem z oryginalnie wygenerowanego.

+0

Więc chcę zweryfikować twoją odpowiedź. Jednak najpierw wypróbowałem proste rozwiązanie aconz2 i zadziałało (również odinstalowałem i ponownie zainstalowałem tifffile z gohike wheel). Teraz, kiedy próbuję powrócić do poprzedniej wersji Pyinstallera (3.2), nie mogę powtórzyć błędu! Jest po prostu naprawiony i pozostaje niezmienny! Bardzo dziwne. Oznacza to, że nie mogę sprawdzić, czy twoja odpowiedź działa, ponieważ wolałabym się nie zastanawiać, czy dokonałem jakiejś innej, przypadkowej zmiany, ponieważ cieszę się, że teraz działa. Tak czy inaczej, dam ci +1, ponieważ prawdopodobnie lepiej wyjaśnisz, skąd pochodzi błąd niż ktokolwiek inny. – Frikster

+0

Brak problemów. Podejrzewam, że nie można ponownie przetestować, ponieważ uaktualniono setuptools/packaging i tam, gdzie znajdowała się odpowiednia poprawka. – muggy