8000 [Bug]: Large image draw performance deterioration in recent releases · Issue #27554 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content
[Bug]: Large image draw performance deterioration in recent releases #27554
Closed
@ilopata1

Description

@ilopata1

Bug summary

Draw performance for large images has deteriorated significantly between Matplotlib 3.1 and 3.8. The biggest issue seems to arise when upgrading from 3.1. to 3.2

Code for reproduction

import cProfile
from profilehooks import profile
import tkinter as tk
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backends.backend_tkagg import (FigureCanvas)
from PIL import Image
import time

class Application(tk.Frame):

        SIZE = 16000

        def __init__(self, master=None):
            
            super().__init__(master)
            self.figure = plt.figure('Diagram')
            self.canvas = FigureCanvas(self.figure, self)
            self.ax = plt.axes()
            self.canvas.mpl_connect('button_press_event', self.toggle)
            
            self.border = 0
            random_image = self.image_gen()
            self.image = self.ax.imshow(random_image)
                 
            self.canvas.get_tk_widget().pack(side=tk.TOP,fill=tk.BOTH, expand=1)
            self.pack(side=tk.TOP,fill=tk.BOTH, expand=1)

        @profile(immediate=True)
        def toggle(self, event):
            self.border = 0 if self.border == Application.SIZE /4 else Application.SIZE /4 
            self.ax.set_xlim(self.border, Application.SIZE - self.border)
            self.ax.set_ylim(self.border, Application.SIZE - self.border)
            t1= time.process_time()
            self.canvas.draw()
            print("Draw time:", time.process_time() - t1)

        def image_gen(self):
            black = np.zeros((int(Application.SIZE / 8) , int(Application.SIZE / 8), 3),dtype='uint8')
            white = np.ones((int(Application.SIZE / 8) , int(Application.SIZE / 8), 3),dtype='uint8') * 255
            col1 = np.concatenate([white, black, white, black, white, black, white, black], axis=0)
            col2  = np.concatenate([black, white, black, white, black, white, black, white], axis=0)
            output = np.concatenate([col1, col2, col1, col2,col1, col2, col1, col2], axis=1)
            im = Image.fromarray(output, 'RGB')
            return im
                      
if __name__ == '__main__':
          
    root = tk.Tk()
    app = Application(master=root)
    root.state('zoomed')
    app.mainloop()

Actual outcome

When running Matplotlib 3.8.2

         35152 function calls (34234 primitive calls) in 6.264 seconds

   Ordered by: cumulative time, internal time, call count
   List reduced from 650 to 40 due to restriction <40>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    6.264    6.264 min_code.py:29(toggle)
        1    0.000    0.000    6.255    6.255 backend_tkagg.py:9(draw)
        1    0.000    0.000    6.175    6.175 backend_agg.py:381(draw)
        1    0.000    0.000    6.168    6.168 artist.py:93(draw_wrapper)
    130/1    0.001    0.000    6.168    6.168 artist.py:54(draw_wrapper)
        1    0
804C
.000    0.000    6.168    6.168 figure.py:3134(draw)
      2/1    0.000    0.000    6.161    6.161 image.py:113(_draw_list_compositing_images)
        1    0.000    0.000    6.161    6.161 _base.py:3005(draw)
        1    0.000    0.000    6.113    6.113 image.py:623(draw)
        1    0.046    0.046    6.107    6.107 image.py:928(make_image)
        1    0.041    0.041    6.061    6.061 image.py:333(_make_image)
        2    3.877    1.939    3.877    1.939 image.py:215(_rgb_to_rgba)
        2    0.000    0.000    2.142    1.071 image.py:160(_resample)
        2    2.141    1.071    2.141    1.071 {built-in method matplotlib._image.resample}
        1    0.000    0.000    0.080    0.080 backend_tkagg.py:13(blit)
        1    0.000    0.000    0.080    0.080 _backend_tk.py:72(blit)
      2/1    0.000    0.000    0.080    0.080 {method 'call' of '_tkinter.tkapp' objects}
        1    0.000    0.000    0.080    0.080 _backend_tk.py:58(_blit)
        1    0.080    0.080    0.080    0.080 {built-in method matplotlib.backends._tkagg.blit}
        2    0.000    0.000    0.042    0.021 axis.py:1379(draw)
       18    0.000    0.000    0.018    0.001 axis.py:287(draw)
       39    0.001    0.000    0.013    0.000 text.py:915(get_window_extent)
        4    0.000    0.000    0.013    0.003 axis.py:1311(_get_ticklabel_bboxes)
        4    0.000    0.000    0.013    0.003 axis.py:1315(<listcomp>)
       54    0.002    0.000    0.013    0.000 text.py:358(_get_layout)
       43    0.001    0.000    0.012    0.000 text.py:734(draw)
        6    0.000    0.000    0.010    0.002 patches.py:579(draw)
        6    0.000    0.000    0.010    0.002 axis.py:1270(_update_ticks)
        6    0.000    0.000    0.010    0.002 patches.py:530(_draw_paths_with_artist_properties)
        6    0.000    0.000    0.009    0.001 backend_agg.py:95(draw_path)
        6    0.009    0.001    0.009    0.001 {method 'draw_path' of 'matplotlib.backends._backend_agg.RendererAgg' objects}
        1    0.000    0.000    0.009    0.009 {built-in method builtins.print}
        4    0.000    0.000    0.009    0.002 run.py:448(write)
        5    0.000    0.000    0.008    0.002 rpc.py:216(remotecall)
        2    0.000    0.000    0.008    0.004 axis.py:2143(_get_tick_boxes_siblings)
        5    0.000    0.000    0.008    0.002 rpc.py:246(asyncreturn)
        5    0.000    0.000    0.008    0.002 rpc.py:290(getresponse)
        5    0.000    0.000    0.008    0.002 rpc.py:306(_getresponse)
        5    0.000    0.000    0.007    0.001 threading.py:280(wait)
        4    0.000    0.000    0.007    0.002 rpc.py:606(__call__)

When running Matplotlib 3.1.3

         29643 function calls (28812 primitive calls) in 3.848 seconds

   Ordered by: cumulative time, internal time, call count
   List reduced from 661 to 40 due to restriction <40>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    3.848    3.848 min_code.py:29(toggle)
        1    0.000    0.000    3.814    3.814 backend_tkagg.py:8(draw)
        1    0.000    0.000    3.552    3.552 backend_agg.py:382(draw)
    130/1    0.001    0.000    3.547    3.547 artist.py:30(draw_wrapper)
        1    0.000    0.000    3.547    3.547 figure.py:1661(draw)
      2/1    0.000    0.000    3.540    3.540 image.py:117(_draw_list_compositing_images)
        1    0.000    0.000    3.540    3.540 _base.py:2573(draw)
        1    0.000    0.000    3.494    3.494 image.py:595(draw)
        1    0.091    0.091    3.490    3.490 image.py:872(make_image)
        1    0.002    0.002    3.399    3.399 image.py:255(_make_image)
        2    3.192    1.596    3.192    1.596 image.py:163(_rgb_to_rgba)
        1    0.000    0.000    0.240    0.240 _backend_tk.py:58(blit)
        1    0.235    0.235    0.235    0.235 {built-in method matplotlib.backends._tkagg.blit}
        2    0.204    0.102    0.205    0.102 {built-in method matplotlib._image.resample}
        2    0.000    0.000    0.040    0.020 axis.py:1195(draw)
        1    0.000    0.000    0.034    0.034 {built-in method builtins.print}
        4    0.000    0.000    0.034    0.008 run.py:354(write)
        5    0.000    0.000    0.033    0.007 rpc.py:217(remotecall)
        5    0.000    0.000    0.032    0.006 rpc.py:247(asyncreturn)
        5    0.000    0.000    0.032    0.006 rpc.py:291(getresponse)
        5    0.000    0.000    0.032    0.006 rpc.py:307(_getresponse)
        5    0.000    0.000    0.032    0.006 threading.py:263(wait)
       10    0.032    0.003    0.032    0.003 {method 'acquire' of '_thread.lock' objects}
        2    0.026    0.013    0.026    0.013 {method 'call' of '_tkinter.tkapp' objects}
        4    0.000    0.000    0.025    0.006 rpc.py:560(__getattr__)
        1    0.000    0.000    0.025    0.025 rpc.py:578(__getmethods)
        1    0.000    0.000    0.021    0.021 __init__.py:1178(update_idletasks)
       18    0.000    0.000    0.015    0.001 axis.py:289(draw)
        4    0.000    0.000    0.015    0.004 axis.py:1074(_update_ticks)
        8    0.000    0.000    0.011    0.001 ticker.py:2079(__call__)
        6    0.000    0.000    0.011    0.002 patches.py:563(draw)
        8    0.000    0.000    0.011    0.001 ticker.py:2083(tick_values)
        8    0.000    0.000    0.011    0.001 ticker.py:2019(_raw_ticks)
        6    0.000    0.000    0.010    0.002 backend_agg.py:119(draw_path)
        6    0.010    0.002    0.010    0.002 {method 'draw_path' of 'matplotlib.backends._backend_agg.RendererAgg' objects}
        8    0.000    0.000    0.009    0.001 axis.py:56(__init__)
        4    0.000    0.000    0.009    0.002 rpc.py:607(__call__)
       43    0.001    0.000    0.009    0.000 text.py:655(draw)
       39    0.000    0.000    0.008    0.000 text.py:852(get_window_extent)
        4    0.000    0.000    0.008    0.002 axis.py:1147(_get_tick_bboxes)

Expected outcome

I would hope that performance remained reasonable consistent or improved between versions.

Additional information

I have only been able to trace this as far as _make_image. The issue seems to persist regardless of interpolation method.

Operating system

Windows 11 64-bit

Matplotlib Version

3.2 upward

Matplotlib Backend

TkAgg

Python version

3.6 for Matplotlib 3.1.3; 3.9 for Matplotlib 3.8.2

Jupyter version

n/A

Installation

conda

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0