0

I'm trying to save a very long EEG line plot (>100 hours with a datapoint every 5min) as a single image with a resolution ~10x10000px so that it would be readable by human eye when zoomed in.

I tried using figsize=(10000,10) but this just gives me an error that the image is too big. Anything in the figsize acceptable range returns a compressed image that is useless for EEG reading:

enter image description here

I also tried using MNE (https://mne.tools/stable/index.html) but I can only get it sight in an interactive window. A soon as I save it as a picture I get this compressed image of the full EEG: enter image description here

What I want instead is something like this: enter image description here

UPDATE:

I've tried both increasing the dpi and using .svg as format. This is the code as requested:

def visualize_eeg(mat_path):
    mat = scipy.io.loadmat(mat_path)
    fs = mat['fs'][0][0]
    ch_labels = [x[0].upper() for x in mat['ch_labels'][0]]
    eeg_data = list(list(x) for x in mat['eeg_data'])
    dict_data = dict(zip(ch_labels,eeg_data))
    df = pd.DataFrame(dict_data)
    df = df.T.sort_index().T
    fig = plt.figure(figsize=(50, 10)) 
    gs = gridspec.GridSpec(len(df.columns), 1,wspace=0.0, hspace=0.0)
    hours = get_hours(df,fs)
    # plot each column (channel) in a subplot
    for i in tqdm(range(len(df.columns))):
        ch = df.columns[i]
        ax = plt.subplot(gs[i])
        ax.spines['top'].set_visible(False)
        ax.spines['right'].set_visible(False)
        ax.set_yticks([0])
        ax.set_yticklabels([ch])
        if i == len(df.columns)-1:
            ax.tick_params(left = True, right = False , labelleft = True ,
                labelbottom = True, bottom = True)
            ax.set_xticks(hours.index,hours)
        else:
            ax.spines['bottom'].set_visible(False)
            ax.tick_params(left = True, right = False , labelleft = True ,
                    labelbottom = False, bottom = False)
        ax.plot(df[ch].apply(lambda x : x*1000), label=ch)
        
    fig.set_dpi(1000)
    fig.savefig('test.svg')

But what I get is just a scrollable crappy image (zoom or not): enter image description here enter image description here

Fabio Magarelli
  • 1,031
  • 4
  • 14
  • 47
  • Please add code that is failing to better answer the question – Ibn Masood Aug 15 '22 at 11:31
  • `figsize=(10000,10)` means a 10000 inch wide and 10 inch tall figure. The resolution depends on both `figsize`, `dpi` and line width. Experiment with, e.g., a 1000 dpi and 1000x1 figure, then reduce the figure size/increase dpi as required. – K.Cl Aug 15 '22 at 11:45
  • Hi there I've added some more details as requested – Fabio Magarelli Aug 15 '22 at 12:33
  • 1
    Does this answer your question? [How to make savefig() save image for 'maximized' window instead of default size](https://stackoverflow.com/questions/10041627/how-to-make-savefig-save-image-for-maximized-window-instead-of-default-size) – Michael S. Aug 15 '22 at 17:15
  • To me, it looks like you have to reduce the linewidth (`ax.plot(df[ch].apply(lambda x : x*1000), label=ch, lw=0.1)` and, perhaps, use `np.clip` on the data to remove those large peaks in the beginning. They're compressing the rest of your data. I did that with the toy data @MichaelS. provided with some noisier data in the beginning (from 1500 to 3500). You still have to fiddle with the settings to get an appropriate figure height and width, and how you're going to clip (I used median +- 5%) – K.Cl Aug 16 '22 at 15:40
  • Just zoom in your y-axis. Do not make a 50-inch wide figure unless you have a 50-inch wide screen. – Jody Klymak Aug 17 '22 at 15:57

2 Answers2

0

Your best bet might be to save the figure as a Scalable Vector Graphic (svg) and then just open it in Chrome or another browser (as they support svg viewing). SVGs are great because you can zoom in as much as you want without losing resolution. This figure has 10,000 points which is 8x larger than your figure (10 hours * 60 min / 5 min interval = 1200 points) and it can be viewed fine.

import matplotlib.pyplot as plt
data = [2515, 2513, 2513, 2513, 2513, 2512, 2515, 2511, 2526, 2513, 2511, 2500, 2521, 2511, 2523, 
        2512, 2529, 2513, 2526, 2514, 2518, 2512, 2524, 2512, 2527, 2512, 2521, 2512, 2517, 2514, 
        2522, 2512, 2521, 2512, 2528, 2511, 2523, 2512, 2518, 2513, 2522, 2512, 2511, 2512, 2524, 
        2512, 2515, 2512, 2509, 2512, 2515, 2512, 2528, 2512, 2516, 2512, 2527, 2512, 2526, 2512, 
        2528, 2512, 2529, 2512, 2523, 2511, 2526, 2512, 2521, 2513, 2510, 2512, 2523, 2513, 2500, 
        2511, 2518, 2512, 2513, 2512, 2526, 2512, 2526, 2512, 2520, 2512, 2526, 2512, 2519, 2500, 
        2529, 2511, 2514, 2512, 2522, 2512, 2513, 2512, 2515, 2512]*100
plt.rcParams["figure.figsize"] = (900,10)
plt.plot(data)
plt.savefig("test.svg")
Michael S.
  • 3,050
  • 4
  • 19
  • 34
0

Thank you for all your answers and comments, in the end, I decided to go back to use MNE, take distinct snapshot of each 10min segment of EEG:

for seg in range(segments):
    fig = raw.plot(start=seg*600, duration=600, show=False, scalings={'eeg':df.std().mean()*2},
    show_scrollbars=False, show_scalebars=False)
    fig.savefig('temp_subplots/%s_%i.png' % (filename,h))
    plt.close()

Then I used Pillow to concatenate the images together to form a single picture of the long trace EEG:

from PIL import Image


def merge_images(filename):
    images = []
    all_images = [x for x in os.listdir('temp_subplots/') if filename in x]
    for i in all_images:
        images.append(Image.open('temp_subplots/%s' % i))
    image_size = images[0].size
    new_image = Image.new('RGB',(image_size[0] * len(images), image_size[1]), (250,250,250))
    for i,img in enumerate(images):
        new_image.paste(img,(img.size[0]*i,0))
    new_image.save("long_plots/%s.png" % filename)
    while not os.path.isfile("long_plots/%s.png" % filename):
        sleep(1)
    shutil.rmtree('temp_subplots')
Fabio Magarelli
  • 1,031
  • 4
  • 14
  • 47