Сохранение изображений ввода-вывода с использованием эффективности и скорости Python

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

У меня есть изображения .bmp размером около 5 МБ каждое. Я хочу взять среднее значение двух изображений и сохранить среднее значение в другом каталоге файлов. Этот ноутбук компании имеет 8 ГБ ОЗУ, 64-разрядную версию, процессор AMD A10-7300 Radeon R6, 10 вычислительных ядер 4C+6G 1,9 ГГц.

Я так и сделал, но теперь мой менеджер-стажер хочет, чтобы я сделал процесс сохранения намного быстрее (сейчас это занимает около 2-3 минут для 500 изображений). Я использую функцию imageResult.save(currentSavePath,"bmp").

Вот код сохранения изображения:

# function for file selection 2
def FileSelect2(self, event):
    dirDialog = wx.DirDialog(self, "Choose a directory:", style=wx.DD_DEFAULT_STYLE);

    # user canceled file opening
    if dirDialog.ShowModal() == wx.ID_CANCEL:
        return

    # otherwise, proceed loading the file chosen by the user
    self.rootDir2 = dirDialog.GetPath()
    self.subdirArray2 = [];
            for dirName, subdirList, fileList in os.walk(self.rootDir2):
                for fname in fileList:
                    if os.path.splitext(fname)[1] == '.bmp':
                        self.subdirArray2.append(dirName+'\\'+fname)

    self.fileDisplay2.Clear()
    self.statusText.SetForegroundColour(wx.BLACK)
    self.blocker = False
    self.fileDisplay2.AppendText(self.rootDir2)
# function for making sure the directory matches
def CheckIfFilesMatch(self):
    if(self.subdirArray1.__len__() != self.subdirArray2.__len__()):
        self.statusText.SetValue("please enter same amount of files")
            self.blocker = True
            self.statusText.SetForegroundColour(wx.RED)
        return False 
    for f in self.subdirArray1:
        if f.replace(self.rootDir1,self.rootDir2) not in self.subdirArray2:
            self.statusText.SetValue("This file: " + f + " does not correspond to any file in parallel.")
                self.blocker = True
                self.statusText.SetForegroundColour(wx.RED)
                return False
        for f in self.subdirArray2:
        if f.replace(self.rootDir2,self.rootDir1) not in self.subdirArray1:
            self.statusText.SetValue("This file: " + f + " does not correspond to any file in parallel.")
                self.blocker = True
                self.statusText.SetForegroundColour(wx.RED)
                return False

функция усреднения изображений

def Average(self, event):
    self.CheckIfFilesMatch()
    if self.blocker:
        return
    self.count = 0
    # save file
    saveDialog = wx.DirDialog(self, "Choose a directory(Your files will be saved in same file names under this):", style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT);
    # cancel
        if saveDialog.ShowModal() == wx.ID_CANCEL:
            # update status
            self.statusText.SetValue("Did not save")
    self.statusText.SetForegroundColour(wx.BLACK)
        # ok
        return

    else:
        savePath = saveDialog.GetPath()
        # start reading file
        for i in self.subdirArray1:
                postfix = i.replace(self.rootDir1, "")
                print postfix
                print i
                f = self.rootDir2+postfix
                if not os.path.isdir(os.path.dirname(savePath+postfix)):
                    os.makedirs(os.path.dirname(savePath+postfix))
                currentSavePath = savePath+postfix
            try:
                # update status
                self.statusText.SetValue("Processing...")
                self.statusText.SetForegroundColour(wx.BLACK)
                # try reading the files
                print "first path: "+i
                print "second path: "+f
                self.im1 = Image.open(i)
                self.im2 = Image.open(f)
                self.count += 1
                # convert to matrix
                self.mat1 = numpy.array(self.im1)
                self.mat2 = numpy.array(self.im2)
                # convert to uint16 for addition
                self.mat1 = self.mat1.astype('uint16')
                self.mat2 = self.mat2.astype('uint16')
                # get offset
                try:
                        self.offset = int(self.offsetCtrl.GetValue())
                except ValueError:
                        #throw error
                        self.statusText.SetValue("Error: please enter integer offset")
                        self.statusText.SetForegroundColour(wx.RED)
                        return
                # add and convert back to image (with offset)
                self.result = (self.mat1 + self.mat2 + self.offset)/2
                self.result[self.result > 255] = 255
                # convert back to uint 8 for saving
                self.result = self.result.astype('uint8')
                self.imResult = Image.fromarray(self.result)
                # self.imResult = Image.blend(self.im1, self.im2, 1)
                    self.imResult.save(currentSavePath,"bmp")
                            # update status
                    self.statusText.SetValue("Saved image to " + currentSavePath)
                    self.statusText.SetForegroundColour(wx.BLACK)
            except IOError:
                # throw error
                self.statusText.SetValue("Error: cannot read file : " + i + " or " + f)
                self.statusText.SetForegroundColour(wx.RED)
    return

2-3 минуты нормально? Можно быстрее? Должен ли я уменьшить разрешение на финальном изображении?


person OneRaynyDay    schedule 10.08.2015    source источник
comment
В вашем методе CheckIfFilesMatch это вложенный цикл for?   -  person smac89    schedule 10.08.2015
comment
@ Smac89 Нет, это не так, это была какая-то ошибка с отступом, когда я копировал и вставлял текст, ха-ха, мой плохой!   -  person OneRaynyDay    schedule 11.08.2015


Ответы (3)


Это больше похоже на ответ codeReview. Кажется, что ваш процесс сохранения изображения довольно быстрый, как заметил Дидье, поэтому я просто предложу некоторые оптимизации для другого процесса. участвует, который является методом CheckIfFilesMatch. Этот фрагмент кода, который сейчас имеет сложность O(N2)

for f in self.subdirArray1:
    if f.replace(self.rootDir1,self.rootDir2) not in self.subdirArray2:
        self.statusText.SetValue("This file: " + f + " does not correspond to any file in parallel.")
        self.blocker = True
        self.statusText.SetForegroundColour(wx.RED)
        return False
for f in self.subdirArray2:
    if f.replace(self.rootDir2,self.rootDir1) not in self.subdirArray1:
        self.statusText.SetValue("This file: " + f + " does not correspond to any file in parallel.")
        self.blocker = True
        self.statusText.SetForegroundColour(wx.RED)
        return False

Вы можете сделать это O (N), создав набор из self.subdirArray1 или self.subdirArray2. Тогда код теперь будет выглядеть так:

def CheckIfFilesMatch(self):
    if(len(self.subdirArray1) != len(self.subdirArray2)):
        self.__FileMatchError("please enter same amount of files")
        return False

    tmp = set(self.subdirArray2)
    for f in self.subdirArray1:
        frev = f.replace(self.rootDir1,self.rootDir2);
        if frev not in tmp:
            self.__FileMatchError("This file: " + f + " does not correspond to any file in parallel.")
            return False
        tmp.discard(frev)
    if tmp:
        self.__FileMatchError("This file: " + tmp.pop() + " does not correspond to any file in parallel.")
        return False
    return True

def __FileMatchError(self, txt):
    self.statusText.SetValue(txt)
    self.blocker = True
    self.statusText.SetForegroundColour(wx.RED)
person smac89    schedule 11.08.2015

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

У вас есть 500 изображений по 5 МБ каждое, и вам нужно прочитать два из них, чтобы записать одно. Итак, вы читаете 500*5*2 = 5 ГБ, а записываете на диск 2,5 ГБ.

Предположим, что это длится 3 минуты. Это означает, что пропускная способность ввода-вывода составляет 27,7 МБ/с в режиме чтения и 13,8 МБ/с в режиме записи. Этот результат не так уж плох для классического вращающегося диска.

Теперь, если у вас есть SSD на этом ноутбуке, это означает, что вы далеки от насыщения пропускной способности ввода-вывода, и, вероятно, вы могли бы добиться большего. Например, вы можете попытаться распараллелить процесс (введя пул потоков).

person Didier Spezia    schedule 10.08.2015

Вы можете ускорить вычисления с помощью графического процессора. 2-3 минуты на 500 изображений — это не так уж и странно, для исследований по обработке больших изображений часто используются выделенные серверы.

Что касается сохранения, то диск здесь является медленным фактором. Используйте для этой цели выделенный стек или перейдите на SSD, если можете.

person zxvn    schedule 10.08.2015