464 lines
18 KiB
Python
Executable File
464 lines
18 KiB
Python
Executable File
# ##### BEGIN GPL LICENSE BLOCK #####
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software Foundation,
|
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
#
|
|
# ##### END GPL LICENSE BLOCK #####
|
|
#
|
|
# This is part of the python program 'AFM_thumbs'. Please, read the
|
|
# full licence text and other comments in the file 'AFM_thumbs.py'
|
|
#
|
|
# Dr. Clemens Barth (barth@root-1.de), project manager.
|
|
#
|
|
# DO NOT REMOVE THIS PREAMBLE !!!
|
|
#
|
|
|
|
import sys
|
|
import os
|
|
import numpy as np
|
|
import re
|
|
|
|
# The following is a bit confusing but needed for crontab and ssh under Linux:
|
|
# Matplotlib needs an X-Server, which does not exist when doing, e.g., ssh.
|
|
# In the following we ask if the DISPLAY variable of the Linux system is set.
|
|
# If this is not the case then we tell Python to do something special for
|
|
# matplotlib (use of 'Agg')
|
|
#
|
|
# From: "http://stackoverflow.com/questions/4931376/
|
|
# generating-matplotlib-graphs-without-a-running-x-server"
|
|
# Other solution: matplotlib API, see "http://www.dalkescientific.com/writings/
|
|
# diary/archive/2005/04/23/matplotlib_without_gui.html"
|
|
if sys.platform == "linux2":
|
|
environ = os.environ
|
|
if "DISPLAY" not in environ:
|
|
import matplotlib as mpl
|
|
mpl.use('Agg')
|
|
import matplotlib.pyplot as plt
|
|
import matplotlib.lines as mpllines
|
|
from matplotlib.colors import LinearSegmentedColormap
|
|
|
|
from AFM_thumbs.Variables import globvar_AFMdata
|
|
from AFM_thumbs.Variables import globvar_AFMdir
|
|
from AFM_thumbs.Variables import globvar_ThumbPara
|
|
from AFM_thumbs.Variables import globvar_thumb_size_x
|
|
from AFM_thumbs.Variables import globvar_thumb_size_y
|
|
from AFM_thumbs.Variables import globvar_thumb_text_width
|
|
from AFM_thumbs.Variables import ID_abbreviations
|
|
from AFM_thumbs.Channel import select_color
|
|
from AFM_thumbs.Channel import invert
|
|
from AFM_thumbs.Colour import reverse_colourmap
|
|
|
|
# _________________________________________________________________ Definitions
|
|
|
|
def create_thumb_images(data, image_nr, image_name, data_file_name):
|
|
|
|
# If there is a meaningful number in 'globvar_ThumbPara.rms':
|
|
if re.match("^\d+?\.\d+?$", globvar_ThumbPara.rms) != None:
|
|
|
|
# We reshape the array first because we can then better analyze the
|
|
# data for the contrast adjustment via the rms value.
|
|
datar = np.reshape(data, data.size)
|
|
|
|
# If there are regions in the image that are on the exact same value
|
|
# then discard them from the statistics analysis! This happens when,
|
|
# e.g., Nanonis images were not stored until the end of a scanning
|
|
# frame.
|
|
where = np.where(np.diff(datar) == 0)
|
|
# At least the length of a scanning line.
|
|
if len(where[0]) > float(globvar_AFMdata.x_pixel):
|
|
datar = np.delete(datar, where)
|
|
|
|
# Put the rms value into a float. The factor of 2.0 is to adapt old
|
|
# values.
|
|
rms = abs(float(globvar_ThumbPara.rms)) * 2.0
|
|
# Average value of the data array
|
|
aver = np.average(datar)
|
|
# Substrat the average value from the data and consider only the
|
|
# the absolute deviations from the average.
|
|
sub = np.abs(np.subtract(datar, aver))
|
|
# From the absolute deviations, calculate the mean and max value.
|
|
sub_aver = np.average(sub)
|
|
sub_max = np.max(sub)
|
|
|
|
# Are there any spikes, so, values that overpass by a factor of 30?
|
|
index = np.argwhere(sub > 30.0 * sub_aver)
|
|
if index.size > 0:
|
|
# We reduce the rms value by a factor of 2.0.
|
|
rms = rms / 2.0
|
|
#print(image_nr, image_name, len(index))
|
|
|
|
# Cut the data on the top and bottom.
|
|
data = np.ma.clip(data, aver-sub_aver*rms, aver+sub_aver*rms)
|
|
|
|
# This deaktivates the interactive pyplot frame
|
|
# plt.ioff()
|
|
|
|
fig = plt.figure(figsize=(globvar_thumb_size_x, globvar_thumb_size_y))
|
|
frame = plt.subplot(111)
|
|
|
|
# Positioning of the image
|
|
plt.subplots_adjust(left=0.08, bottom=None,
|
|
right=0.99, top=1.1,
|
|
wspace=None, hspace=None)
|
|
|
|
# Image is put in
|
|
try:
|
|
float(globvar_AFMdata.x_size)
|
|
except ValueError:
|
|
globvar_AFMdata.x_size = "-100.0"
|
|
|
|
try:
|
|
float(globvar_AFMdata.y_size)
|
|
except ValueError:
|
|
globvar_AFMdata.y_size = "-100.0"
|
|
|
|
imgage = frame.imshow(data,extent=[0,abs(float(globvar_AFMdata.x_size)),
|
|
0,abs(float(globvar_AFMdata.y_size))])
|
|
|
|
# What kind of color scale do we have?
|
|
# Is it a pre-definied color from matplotlib, then it must be a string.
|
|
if type(select_color(image_nr, globvar_AFMdata.scandir[image_nr])) is str:
|
|
colormap = select_color(image_nr, globvar_AFMdata.scandir[image_nr])
|
|
|
|
if invert(image_nr,globvar_AFMdata.scandir[image_nr]):
|
|
colormap = reverse_colourmap("ListedColormap", colormap)
|
|
|
|
# If it is not a string, then it must be a color definied by ourselves,
|
|
# so go to here and transform this linear segment into a colormap.
|
|
else:
|
|
colormap = LinearSegmentedColormap("Colormap",select_color(image_nr,
|
|
globvar_AFMdata.scandir[image_nr]))
|
|
|
|
if invert(image_nr,globvar_AFMdata.scandir[image_nr]):
|
|
colormap = reverse_colourmap("LinearSegmentedColormap", colormap)
|
|
|
|
# Color scale
|
|
imgage.set_cmap(colormap)
|
|
|
|
if globvar_ThumbPara.peek:
|
|
imgage.set_clim(vmin=np.amin(data), vmax=np.amax(data))
|
|
|
|
# The colorbar
|
|
colorbar = fig.colorbar(imgage, shrink=0.65, aspect=30)
|
|
for t in colorbar.ax.get_yticklabels():
|
|
t.set_fontsize('x-small')
|
|
|
|
# The ticks and the size of the labels
|
|
plt.xticks(size = 'x-small')
|
|
plt.yticks(size = 'x-small')
|
|
|
|
# The ticks and their orientation
|
|
# ... at the bottom
|
|
lines = frame.get_xticklines()
|
|
labels = frame.get_xticklabels()
|
|
for line in lines:
|
|
line.set_marker(mpllines.TICKDOWN)
|
|
for label in labels:
|
|
label.set_y(-0.02)
|
|
# ... on the left
|
|
lines = frame.get_yticklines()
|
|
labels = frame.get_yticklabels()
|
|
for line in lines:
|
|
line.set_marker(mpllines.TICKLEFT)
|
|
for label in labels:
|
|
label.set_x(-0.02)
|
|
|
|
# Some text into the figure
|
|
# 1. Scanning direction to the bottom right
|
|
if globvar_AFMdata.scandir[image_nr] == "forward":
|
|
fig.text(0.83, 0.17, "forward", fontsize=10)
|
|
if globvar_AFMdata.scandir[image_nr] == "backward":
|
|
fig.text(0.83, 0.17, "backward", fontsize=10)
|
|
|
|
# 2. Channel to the top right
|
|
fig.text(0.83, 0.95,
|
|
globvar_AFMdata.channel[image_nr] + " (" + \
|
|
globvar_AFMdata.unit[image_nr] + ")",
|
|
fontsize=11)
|
|
|
|
# 3. Text with parameters below the image
|
|
fig.text(0.035 + float(globvar_ThumbPara.text_x),
|
|
0.015 + float(globvar_ThumbPara.text_y),
|
|
image_text(image_name),
|
|
fontname=globvar_ThumbPara.font,
|
|
size=int(globvar_ThumbPara.text_size))
|
|
|
|
# 4. Average value below color scale
|
|
data_average = globvar_AFMdata.z_average
|
|
data_average = str(data_average)
|
|
data_average = data_average[:data_average.find(".")+2]
|
|
fig.text(0.83, 0.2,
|
|
"Aver. (raw)\n" + data_average + " (" + \
|
|
globvar_AFMdata.unit[image_nr] + ")",
|
|
fontsize=10)
|
|
|
|
# Save image now !
|
|
image_path = os.path.join(globvar_AFMdir.thumbnail_directory, image_name)
|
|
|
|
if globvar_ThumbPara.png:
|
|
image_path=image_path+".png"
|
|
else:
|
|
image_path=image_path+".jpeg"
|
|
|
|
plt.savefig(image_path, dpi=int(globvar_ThumbPara.res))
|
|
|
|
#plt.close('all')
|
|
plt.close(fig)
|
|
plt.clf()
|
|
|
|
|
|
def image_text(image_name):
|
|
|
|
text_string = image_name + ", "
|
|
|
|
if globvar_ThumbPara.date and globvar_AFMdata.date != "":
|
|
text_string += "--"+globvar_AFMdata.date+"--, "
|
|
if globvar_ThumbPara.sizenm and globvar_AFMdata.x_size != "" and \
|
|
globvar_AFMdata.y_size != "":
|
|
text_string += "Size: " + globvar_AFMdata.x_size + "x" + \
|
|
globvar_AFMdata.y_size + ", "
|
|
if globvar_ThumbPara.sizepx and globvar_AFMdata.x_pixel != "" and \
|
|
globvar_AFMdata.y_pixel != "":
|
|
text_string += "Pixl: " + globvar_AFMdata.x_pixel + "x" + \
|
|
globvar_AFMdata.y_pixel + ", "
|
|
if globvar_ThumbPara.offset and globvar_AFMdata.x_off != "" and \
|
|
globvar_AFMdata.y_off != "":
|
|
text_string += "Off: " + globvar_AFMdata.x_off + "x" + \
|
|
globvar_AFMdata.y_off + ", "
|
|
# SPECS Nanonis
|
|
# =============
|
|
# So far, there is no option(button). This needs to be implemented in future.
|
|
if globvar_AFMdata.res_freq != "":
|
|
text_string += "f0: " + globvar_AFMdata.res_freq + ", "
|
|
if globvar_ThumbPara.gain and globvar_AFMdata.gain != "":
|
|
text_string += "Gain: " + globvar_AFMdata.gain + ", "
|
|
if globvar_ThumbPara.gain and globvar_AFMdata.gain_int != "":
|
|
text_string += "IGain: " + globvar_AFMdata.gain_int + ", "
|
|
if globvar_ThumbPara.gain and globvar_AFMdata.gain_prop != "":
|
|
text_string += "PGain: " + globvar_AFMdata.gain_prop + ", "
|
|
if globvar_ThumbPara.gain and globvar_AFMdata.gain_signal != "":
|
|
text_string += "SigGain: " + globvar_AFMdata.gain_signal + ", "
|
|
if globvar_ThumbPara.gain and globvar_AFMdata.gain_xy != "":
|
|
text_string += "XYGain: " + globvar_AFMdata.gain_xy + ", "
|
|
if globvar_ThumbPara.gain and globvar_AFMdata.gain_z != "":
|
|
text_string += "ZGain: " + globvar_AFMdata.gain_z + ", "
|
|
|
|
# SPECS Nanonis
|
|
# =============
|
|
# So far, there is no option (button). This needs to be implemented
|
|
# in future.
|
|
if globvar_AFMdata.scan_updown != "":
|
|
text_string += globvar_AFMdata.scan_updown.capitalize() + " scan, "
|
|
if globvar_ThumbPara.speed and globvar_AFMdata.speed != "":
|
|
# For Dulcinea
|
|
if " Hz" in globvar_AFMdata.speed:
|
|
speed = globvar_AFMdata.speed
|
|
text_string += speed
|
|
# Otherwise: we now use the scan frequency as a speed because it is
|
|
# more readable. (2021-02-05)
|
|
else:
|
|
speed = float(globvar_AFMdata.speed) / float(globvar_AFMdata.x_size)
|
|
text_string += "Speed: %.2f" % speed + ", "
|
|
if globvar_ThumbPara.ampl and globvar_AFMdata.amplitude != "":
|
|
text_string += "Ampl: " + globvar_AFMdata.amplitude + ", "
|
|
if globvar_ThumbPara.angle and globvar_AFMdata.angle != "":
|
|
text_string += "Phi: " + globvar_AFMdata.angle + ", "
|
|
# Change 2017-06-25
|
|
# 1. Ugap and Feedback are listed at the end!
|
|
if globvar_ThumbPara.voltage and globvar_AFMdata.voltage != "":
|
|
text_string += "Ugap: " + globvar_AFMdata.voltage + ", "
|
|
if globvar_ThumbPara.feedback and globvar_AFMdata.feedback != "":
|
|
text_string += "FB: " + globvar_AFMdata.feedback
|
|
|
|
# This loop is for building a block of the text introducing some "\n"
|
|
zaehler1 = 0
|
|
zaehler2 = 0
|
|
text_block = ""
|
|
|
|
block_length = globvar_thumb_text_width + int(globvar_ThumbPara.text_block)
|
|
|
|
while (zaehler1 < len(text_string)):
|
|
|
|
if (text_string[zaehler1] == " "):
|
|
if (zaehler2 > block_length):
|
|
text_block = text_block + "\n"
|
|
zaehler2 = 0
|
|
else:
|
|
text_block = text_block + text_string[zaehler1]
|
|
|
|
else:
|
|
text_block = text_block + text_string[zaehler1]
|
|
|
|
zaehler1 +=1
|
|
zaehler2 +=1
|
|
|
|
return text_block
|
|
|
|
|
|
# The function, which creates the thumbnails for spectra
|
|
def create_thumb_spectra(x_data_list,
|
|
y_data_list,
|
|
image_nr,
|
|
image_name,
|
|
image_path,
|
|
channel_name):
|
|
|
|
# Create figure
|
|
fig = plt.figure(figsize=(globvar_thumb_size_x,
|
|
globvar_thumb_size_y))
|
|
|
|
# Positioning of the graph inside the figure
|
|
plt.subplots_adjust(left=0.17, bottom=0.3,
|
|
right=0.95, top=0.95,
|
|
wspace=None, hspace=None)
|
|
|
|
# Plot the whole stuff
|
|
for x_data, y_data in zip(x_data_list, y_data_list):
|
|
plt.plot(x_data, y_data)
|
|
|
|
# Labels
|
|
plt.xlabel(globvar_AFMdata.spec_x_label[image_nr] + \
|
|
" ("+globvar_AFMdata.spec_x_unit[image_nr]+")")
|
|
plt.ylabel(globvar_AFMdata.spec_y_label[image_nr] + \
|
|
" ("+globvar_AFMdata.spec_y_unit[image_nr]+")")
|
|
|
|
if channel_name in ID_abbreviations:
|
|
channel_name = ID_abbreviations[channel_name]
|
|
|
|
# Some text ...
|
|
# 1. text below the spectra
|
|
fig.text(0.035 + float(globvar_ThumbPara.text_x),
|
|
0.035 + float(globvar_ThumbPara.text_y),
|
|
spectra_text(image_name,image_nr),
|
|
fontname=globvar_ThumbPara.font,
|
|
size=int(globvar_ThumbPara.text_size))
|
|
|
|
# The channel, measurement number or whatever to the bottom right
|
|
fig.text(0.92, 0.32,
|
|
channel_name,
|
|
fontsize=13,
|
|
horizontalalignment="right")
|
|
# The x, y, z(z_offset) position to the bottom left
|
|
if globvar_AFMdata.spec_position != []:
|
|
fig.text(0.92, 0.36,
|
|
globvar_AFMdata.spec_position[image_nr],
|
|
fontsize=13,
|
|
horizontalalignment="right")
|
|
|
|
# Save image now !
|
|
plt.savefig(image_path, dpi=int(globvar_ThumbPara.res))
|
|
plt.close(fig)
|
|
plt.clf()
|
|
# Create a white thumbnail image. This is needed as a space filler for
|
|
# the pdfs afterwards ... . The 'white' thumbnail is created only once.
|
|
# Name: 'dummy_image.png'. The path to the thumbnail:
|
|
image_dummy_path=os.path.join(globvar_AFMdir.thumbnail_directory,
|
|
"dummy_image")
|
|
# If it does not exist, create it.
|
|
if not os.path.isfile(image_dummy_path+".jpeg"):
|
|
fig=plt.figure(figsize=(globvar_thumb_size_x,globvar_thumb_size_y))
|
|
plt.savefig(image_dummy_path, dpi=int(globvar_ThumbPara.res))
|
|
plt.close(fig)
|
|
plt.clf()
|
|
|
|
return True
|
|
|
|
|
|
# The text below a spectrum. The routine has been modified on 2022-01-29,
|
|
# for in particular the Nanonis spectroscopy.
|
|
def spectra_text(image_name, channel):
|
|
|
|
# Cut the name if it has become too long!
|
|
if len(image_name) > 30:
|
|
image_name = image_name[:30] + "..."
|
|
|
|
text_string = image_name + ", "
|
|
|
|
if globvar_ThumbPara.date:
|
|
if globvar_AFMdata.date != "":
|
|
text_string += "--"+str(globvar_AFMdata.date)+"--, "
|
|
|
|
if globvar_AFMdata.spec_points != []:
|
|
if globvar_AFMdata.spec_points[channel] != "":
|
|
text_string += "Points: "+str(globvar_AFMdata.spec_points[channel])+", "
|
|
|
|
if globvar_AFMdata.spec_feedback != []:
|
|
if globvar_AFMdata.spec_feedback[channel] != "":
|
|
text_string += "Feedback: " + \
|
|
str(globvar_AFMdata.spec_feedback[channel])+", "
|
|
|
|
if globvar_AFMdata.spec_acquisition != []:
|
|
if globvar_AFMdata.spec_acquisition[channel] != "":
|
|
text_string += "Acq.time: "+str(float(globvar_AFMdata.spec_acquisition \
|
|
[channel])*1000.0)+"ms, "
|
|
|
|
if globvar_AFMdata.spec_delay != []:
|
|
if globvar_AFMdata.spec_delay[channel] != "":
|
|
text_string += "Delay: "+str(float(globvar_AFMdata.spec_delay \
|
|
[channel])*1000.0)+"ms, "
|
|
|
|
if globvar_AFMdata.spec_time != []:
|
|
if globvar_AFMdata.spec_time[channel] != "":
|
|
text_string += "Time: "+str(globvar_AFMdata.spec_time[channel]) + "ms, "
|
|
|
|
if globvar_ThumbPara.gain:
|
|
if globvar_AFMdata.gain != "":
|
|
text_string += "Gain: "+str(globvar_AFMdata.gain)+", "
|
|
if globvar_ThumbPara.ampl:
|
|
if globvar_AFMdata.amplitude != "":
|
|
text_string += "Ampl: "+str(globvar_AFMdata.amplitude)+"nm, "
|
|
|
|
# Other spectroscopy data that shall be displayed.
|
|
if globvar_AFMdata.spec_other != None:
|
|
text_string += globvar_AFMdata.spec_other
|
|
|
|
# Change 2017-06-25
|
|
# 1. Ugap and Feedback are listed at the end!
|
|
if globvar_ThumbPara.voltage:
|
|
if globvar_AFMdata.voltage != "":
|
|
text_string += "Ugap: "+str(globvar_AFMdata.voltage)+", "
|
|
if globvar_ThumbPara.feedback:
|
|
if globvar_AFMdata.feedback != "":
|
|
text_string += "FB: "+str(globvar_AFMdata.feedback)
|
|
|
|
# This while loop is for building a block of the text introducing
|
|
# some "\n"
|
|
zaehler1 = 0
|
|
zaehler2 = 0
|
|
text_block = ""
|
|
|
|
block_length = 50
|
|
|
|
while (zaehler1 < len(text_string)):
|
|
|
|
if (text_string[zaehler1] == " "):
|
|
if (zaehler2 > block_length):
|
|
text_block = text_block + "\n"
|
|
zaehler2 = 0
|
|
else:
|
|
text_block = text_block + text_string[zaehler1]
|
|
else:
|
|
text_block = text_block + text_string[zaehler1]
|
|
zaehler1 +=1
|
|
zaehler2 +=1
|
|
|
|
# If some variables are simply not available, then it might be that there
|
|
# is a ',' at the end. Remove the ','.
|
|
if text_block[-1] == ',':
|
|
text_block = text_block[:-1]
|
|
if text_block[-2] == ',':
|
|
text_block = text_block[:-2]
|
|
|
|
return text_block
|