2017-01-11 15 views
2

Я работаю с tif-стеками и QImage, кажется, искажает некоторые изображения под углом 45 градусов. Matplotlib может отображать изображения без проблем в обоих тестовых случаях (ссылки на два стека tif приведены ниже), поэтому я не думаю, что я где-то прикрутил свой массив.QImage искажает некоторые изображения, но не другие

Вот рабочий пример: (Примечание: этот пример показывает только первое изображение в TIF стеки для простоты)

import matplotlib.pyplot as plt 
import sys 
from PIL import Image 
from PyQt5.QtGui import QPixmap, QImage 
from PyQt5.QtWidgets import (QMainWindow, QApplication, QVBoxLayout, 
          QWidget, QFileDialog, QGraphicsPixmapItem, QGraphicsView, 
          QGraphicsScene) 

import numpy as np 


class Example(QMainWindow): 
    def __init__(self): 
     super().__init__() 

     self.initUI() 


    def initUI(self): 
     # set up a widget to hold a pixmap 
     wid = QWidget(self) 
     self.setCentralWidget(wid) 
     self.local_grview = QGraphicsView() 
     self.local_scene = QGraphicsScene() 
     vbox = QVBoxLayout()     
     self.local_grview.setScene(self.local_scene) 
     vbox.addWidget(self.local_grview) 
     wid.setLayout(vbox) 

     # load and display the image 
     self.loadImage() 

     # display the widget 
     self.show() 

     # also use matplotlib to display the data as it should appear 
     plt.imshow(self.dataUint8[0], cmap='gray') 
     plt.show() 


    def loadImage(self): 
     fname = QFileDialog.getOpenFileName(self, 'Open file', '/home')[0] 

     # use the tif reader to read in the tif stack 
     self.data = self.readTif(fname) 

     # convert to uint8 for display 
     self.dataUint8 = self.uint8Convert(self.data) 

     ############################################################################################################################### 
     # I suspect this is where something goes wrong 
     ############################################################################################################################### 
     # create a QImage object 
     self.im = QImage(self.dataUint8[0], self.dataUint8[0].shape[1], self.dataUint8[0].shape[0], QImage.Format_Grayscale8) 
     # if we save using self.im.save() we also have a skewed image 
     ############################################################################################################################### 

     # send the QImage object to the pixmap generator 
     self.pixmap = QPixmap(self.im) 


     self.pixMapItem = QGraphicsPixmapItem(self.pixmap, None) 
     self.local_scene.addItem(self.pixMapItem) 

    def readTif(self, filename): # use this function to read in a tif stack and return a 3D numpy array 
     # read in the file 
     stack = Image.open(filename)  

     # extract each frame from the file and store in the frames variable 
     frames = [] 
     i = 0 
     while True: 
      try: 
       stack.seek(i) # move to the ith position in the stack 
       frames.append(np.array(stack)) 
       i += 1 
      except EOFError: 
       # end of stack 
       break 
     del stack # probably unnecessary but this presumably saves a bit of memory 

     return frames 


    def uint8Convert(self, frames): # use this function to scale a 3D numpy array of floats to 0-255 so it plays well with Qt methods 

     # convert float array to uint8 array 
     if np.min(frames)<0: 
      frames_uint8 = [np.uint8((np.array(frames[i]) - np.min(frames[i]))/np.max(frames[i])*255) for i in range(np.shape(frames)[0])] 
     else: 
      frames_uint8 = [np.uint8(np.array(frames[i])/np.max(frames[i])*255) for i in range(np.shape(frames)[0])] 

     return frames_uint8 


if __name__=='__main__': 
    app = QApplication(sys.argv) 
    ex = Example() 
    sys.exit(app.exec_()) 

Вот скриншот выхода:

Qimage против Matplotlib

enter image description here

Вот ссылка на стек tif, который отображается правильно:

https://drive.google.com/uc?export=download&id=0B9EG5AHWC9qzX3NrNTJRb2toV2c

А вот ссылка на TIF стек, который становится искажено при отображении:

https://drive.google.com/uc?export=download&id=0B9EG5AHWC9qzbFB4TDU4c2x1OE0

Любая помощь понять, почему QImage искажая этот образ будет высоко ценится. Единственное существенное различие между двумя tif-стеками заключается в том, что тот, который отображает перекосы, имеет простую черную область (нули) вокруг изображения, что делает массив более крупным.

ОБНОВЛЕНИЕ: теперь я обнаружил, что если я обрезаю нарушающее изображение на 1024x1024 или 512x512 или 1023x1024, то отображается QImage, но обрезание на 1024x1023 отображается искаженным. Таким образом, кажется, что длина x (горизонтальная) должна быть мощностью 2 для того, чтобы QImage обрабатывал ее, как ожидалось. Это смехотворное ограничение! Должно быть, я не понимаю. Разумеется, есть способ для обработки массивов произвольной формы.

... Я полагаю, в принципе, можно было бы применить первый перекос к изображению и пусть QImage выпрямлять его обратно ... (< == не поклонник этого решения)

+1

@ekhumoro Согласен! Это был мой первый опыт использования этого сайта для хостинга файлов. Я не буду использовать их снова. Я обновил ссылки для прямой загрузки. Я также не понимал, насколько массивными были эти файлы, эти новые намного легче. Для загрузки требуется только второй или две. – onjre

+0

Спасибо. FWIW, я могу воспроизвести проблему, но я боюсь, что у меня нет никакой информации о причине на данный момент. – ekhumoro

+0

Использовать QImage (fname). Tiff поддерживается QImage. – eyllanesc

ответ

1

Большое спасибо bnaecker за 32-битный выровненный намек и предоставили ссылку на источник. Вот решение.

QImage необходимо знать, сколько байтов в строке массива, иначе оно просто угадает (а в некоторых случаях оно ошибочно). Таким образом, используя следующую функцию в функции loadImage(), выдается правильный вывод.

# get the shape of the array 
nframes, height, width = np.shape(self.dataUint8) 

# calculate the total number of bytes in the frame 
totalBytes = self.dataUint8[0].nbytes 

# divide by the number of rows 
bytesPerLine = int(totalBytes/height) 

# create a QImage object 
self.im = QImage(self.dataUint8[0], width, height, bytesPerLine, QImage.Format_Grayscale8) 

Остальная часть кода то же самое.

1

Изображение не перекошен, базовые данные интерпретируются неправильно.

В используемом вами конструкторе буфер данных является плоским, и вы должны также указать размер строки и столбца в пикселях. Вы как-то указали строки как слишком длинные, так что начало следующей строки завернуто в конец текущей. Вот почему вы получаете «полосатый» образ и почему происходит постепенное увеличение суммы, когда вы попадаете в более поздние строки. Это также объясняет, почему это работает, когда вы используете версию конструктора QImage(fname). Этот конструктор использует код библиотеки Qt для чтения данных изображения, который не имеет проблемы с вашим собственным кодом.

Есть несколько мест, где данные могут быть считаны некорректно. Я не знаю деталей пакета PIL, но линия np.array(stack) выглядит правдоподобным кандидатом. Я не знаю, как объект стека предоставляет интерфейс буфера, но он может делать это иначе, чем вы думаете, например, данные являются столбцами, а не строковыми. Также обратите внимание, что созданный вами конструктор QImage ожидает, что данные будут выровнены по 32 бита, даже для 8-битных данных. Это может быть проблемой.

Другим вероятным кандидатом является метод uint8Convert, который может непреднамеренно переносить данные или иным образом переворачивать их вперед/назад. Возможно, поэтому квадратные размеры работают, а прямоугольные - нет.

+0

Чтобы быть ясным, я могу получить прямоугольник и квадрат для работы или сбоя. Так что это не прямоугольник или квадрат. Кроме того, я проверяю с помощью 'matplotlib', что сам массив не является« чередованием », поэтому я не думаю, что' np.array (stack) 'или' uint8Convert' испортили массив (построение «matplotlib» завершено после вызова обоих этих методов). Но ... 32-битное согласованное требование звучит как возможная проблема. Я не совсем понимаю, что это значит. Не могли бы вы уточнить? – onjre

+1

Прохладный, я согласен с вами в том, что вызов 'np.array()' 'и ваш' uint8Convert' метод, вероятно, в порядке. Ограничение выравнивания означает, что каждая строка данных должна начинаться с 32-разрядной границы в памяти. Кажется, это проблема. Я вырыл (* deep *) в источник Qt C++, и кажется правдоподобным, что это вызовет проблемы. (См. Здесь, чтобы начать, https://code.woboq.org/qt5/qtbase/src/gui/image/qimage.cpp.html#804, и я могу отправить более подробную информацию, если вам нужно.) Что произойдет, если вы 't использовать 8-битные данные, но использовать 32-битные? – bnaecker

+0

Спасибо за ссылку. Это помогло мне разобраться. Существует опция указания количества байтов на строку в массиве. Предоставление этого значения устраняет проблему. Я отправлю код ... – onjre

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

  • Нет связанных вопросов^_^