【问题标题】:Image Saving I/O Using Python Efficiency & Speed使用 Python 效率和速度的图像保存 I/O
【发布时间】:2015-08-10 19:01:57
【问题描述】:

我是 Python 新手(并且有一段时间没有使用文件 IO),所以如果我在我所说的任何内容中犯了初学者的错误,请多多包涵。

我有每个大约 5 MB 的 .bmp 图像。我想取两个图像的平均值并将平均值保存在另一个文件目录中。这家公司的笔记本电脑是 8 GB RAM,64 位,处理器是 AMD A10-7300 Radeon R6,10 Compute Cores 4C+6G 1.9 GHz

我就是这样做的,但现在我的实习经理希望我加快保存过程(现在 500 张图像大约需要 2-3 分钟)。我正在使用函数 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分钟正常吗?能不能快点?我应该降低最终图像的分辨率吗?

【问题讨论】:

  • 在您的 CheckIfFilesMatch 方法中,这是一个嵌套的 for 循环吗?
  • @Smac89 不,不是,这是我复制粘贴文本时出现的缩进错误,哈哈,我的错!

标签: python io


【解决方案1】:

您可以计算它所代表的总 IO 工作负载。

您有 500 张图片,每张 5 MB,您需要读取其中两张才能写入一张。所以你读取 500*5*2 = 5 GB,然后在磁盘上写入 2.5 GB。

假设它持续 3 分钟。这意味着 I/O 吞吐量在读取模式下为 27.7 MB/s,在写入模式下为 13.8 MB/s。这个结果对于经典的旋转盘来说还不错。

现在,如果您在这台笔记本电脑上安装了 SSD,这意味着您的 I/O 带宽还远未达到饱和,或许您可以做得更好。例如,您可以尝试并行化进程(通过引入线程池)。

【讨论】:

    【解决方案2】:

    这更像是一个 codeReview 答案。看起来您的图像保存过程正如Didier 所观察到的那样非常快,所以我将针对所涉及的其他过程提出一些优化,即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
    

    您可以通过从 self.subdirArray1self.subdirArray2 创建一个集合来使其成为 O(N)。然后代码现在看起来像:

    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)
    

    【讨论】:

      【解决方案3】:

      您或许可以使用 GPU 加快计算速度。 500 张图像 2-3 分钟并不奇怪,对于大型图像处理研究,通常使用专用服务器。

      至于保存,磁盘是这里的慢因素。为此目的使用专用堆栈或如果可以更改为 SSD。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-09-05
        • 2013-06-26
        • 2017-03-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-06-13
        • 1970-01-01
        相关资源
        最近更新 更多