Files
AFM_thumbs/AFM_thumbs/Thumbs.py
2022-04-08 17:39:57 +02:00

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