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

675 lines
26 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 struct
import numpy
import math
import re
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_spaces
from AFM_thumbs.Variables import RHK_must_exist
from AFM_thumbs.Variables import RHK_PositionCounter
from AFM_thumbs.Variables import globvar_thumb_size_x
from AFM_thumbs.Variables import globvar_thumb_size_y
from AFM_thumbs.Linalg import check_image_properties
from AFM_thumbs.Thumbs import create_thumb_images
from AFM_thumbs.Thumbs import create_thumb_spectra
from AFM_thumbs.Thumbs import spectra_text
from AFM_thumbs.Linalg import linalg_plane_fit
from AFM_thumbs.Linalg import linalg_line_fit
from AFM_thumbs.Initialize import initialize_AFM_data
from AFM_thumbs.Channel import proceed_with
from AFM_thumbs.Channel import line_fit
from AFM_thumbs.Channel import plane_fit
from AFM_thumbs.Channel import rename_channel
# Method to determine whether there are RHK files.
def is_RHK_file(directory, files):
list_directories_sm4 = []
# A directroy is only interesting if there is at least
# one SM4 file.
FLAG = False
for filename in files:
file_name = os.path.join(directory, filename)
if file_name[-4:] in RHK_must_exist:
FLAG = True
if FLAG == True:
if os.path.isdir(directory):
list_directories_sm4.append(directory)
break
return list_directories_sm4
# This method reads out the scan information.
# RHK technology stores all measurement data in one file with no order!
# Because of this the plane- and linefitting settings doesn't matter in the
# current version of this method
def read_RHK_data(SM4_file):
# _______________________________________________________ Direction check
initialize_AFM_data()
sm4_file_path = os.path.join(globvar_AFMdir.working_directory,SM4_file)
with open(sm4_file_path, "rb") as sm4_datafile: # open file
sm4_file = sm4_datafile.read()
globvar_AFMdata.datfile.append(sm4_file_path)
# ___________________________________________ Reading of the File Header
# Version Check
current_position = 38
version = sm4_file[0:current_position]
if not version[2:-2:2] == "STiMage 005.004 1":
version_error = "This method was written on base of SM4 Version " \
"\'STiMage 005.004 1\'. The results of any other " \
"version can differ from the expected one. The current " \
"file version: "
print(globvar_spaces + version_error + version.decode("utf-8"))
print
# File Header information
total_page_count = struct.unpack('i', sm4_file[current_position: \
current_position+4])[0]
current_position += 4
object_list_count = struct.unpack('i', sm4_file[current_position: \
current_position+4])[0]
current_position += 4
# Check the Ocject Field size
if not struct.unpack('i', sm4_file[current_position: \
current_position+4])[0] == 12:
obJect_field_error = "Wrong size of object field. " \
"This may cause a problem."
print(globvar_spaces + obJect_field_error)
print
current_position += 12
# Create Array for the Object List Items
objects = numpy.zeros((object_list_count, 3))
for i in range(object_list_count):
objects[i,0] = struct.unpack('i', sm4_file[current_position: \
current_position+4])[0] # tape
current_position += 4
objects[i,1] = struct.unpack('i', sm4_file[current_position: \
current_position+4])[0] # offset
current_position += 4
objects[i,2] = struct.unpack('i', sm4_file[current_position: \
current_position+4])[0] # size
current_position += 4
# _________________________________________ Content of PAGE INDEX HEADER
# Page Count Check
page_count = struct.unpack('i', sm4_file[current_position: \
current_position+4])[0]
current_position += 4
if not page_count == total_page_count:
page_count_error = "The total number of pages is not the same as " \
"the number of pages. Some data may be left out or doubled."
print(globvar_spaces + page_count_error)
print
page_object_list_count = struct.unpack('i', sm4_file[current_position: \
current_position+4])[0]
current_position += 4
current_position += 8
# Page Index Object
index_array = numpy.zeros((page_object_list_count, 3))
for i in range(page_object_list_count):
index_array[i,0] = struct.unpack('i', sm4_file[current_position: \
current_position+4])[0];
current_position += 4
# beginning of the Page index Array
index_array[i,1] = struct.unpack('i', sm4_file[current_position: \
current_position+4])[0];
current_position += 4
index_array[i,2] = struct.unpack('i', sm4_file[current_position: \
current_position+4])[0];
current_position += 4
# Page ID Offset
pageindex_page_id_offset = 16 # Page ID hat Laenge 16
# for-loop over all channels
for channel in range(page_count):
# Determining the startpoint
if channel is 0:
next_page_index_array_offset = 0
current_position = int(index_array[0,1]) + pageindex_page_id_offset
else:
current_position = int(next_page_index_array_offset) \
+ pageindex_page_id_offset
# Page Index Array
current_position += 4
# Page Type
page_data_type = struct.unpack('i', sm4_file \
[current_position:current_position+4])[0]
current_position += 4
pageindex_object_list_count = struct.unpack('i', sm4_file \
[current_position:current_position+4])[0]
current_position += 4
current_position += 4
# Create an array for the Object List Items
array_objects = numpy.zeros((pageindex_object_list_count, 3))
for i in range(pageindex_object_list_count):
array_objects[i,0] = struct.unpack('i', sm4_file[current_position \
:current_position+4])[0] # tape
current_position += 4
array_objects[i,1] = struct.unpack('i', sm4_file[current_position \
:current_position+4])[0] # offset
current_position += 4
array_objects[i,2] = struct.unpack('i', sm4_file[current_position \
:current_position+4])[0] # size
current_position += 4
# Request of the current position for the start of the next loop
next_page_index_array_offset = current_position
# Page header
current_position = int(array_objects[0,1])+4
page_type = str(struct.unpack('i', sm4_file[current_position: \
current_position+4])[0])
# Pixel
current_position = int(array_objects[0,1])+24
globvar_AFMdata.x_pixel = str(struct.unpack('i',
sm4_file[current_position:current_position+4])[0])
current_position += 4
globvar_AFMdata.y_pixel = str(struct.unpack('i',
sm4_file[current_position:current_position+4])[0])
current_position += 4
# Scan direction
current_position = int(array_objects[0,1])+36
scan_direction = struct.unpack('i',
sm4_file[current_position:current_position+4])[0];
current_position += 4
if scan_direction == 0:
globvar_AFMdata.scandir.append("right")
elif scan_direction == 1:
globvar_AFMdata.scandir.append("left")
elif scan_direction == 2:
globvar_AFMdata.scandir.append("up")
elif scan_direction == 3:
globvar_AFMdata.scandir.append("down")
current_position += 4
data_size = struct.unpack('i', sm4_file[current_position: \
current_position+4])[0]
current_position += 4
# Maximum z-Values
globvar_AFMdata.z_min.append(struct.unpack('=l',
sm4_file[current_position:current_position+4])[0])
current_position += 4
globvar_AFMdata.z_max.append(struct.unpack('=l',
sm4_file[current_position:current_position+4])[0])
current_position += 4
# Sizes
globvar_AFMdata.x_size = struct.unpack('f', sm4_file[current_position:\
current_position+4])[0]
current_position += 4
globvar_AFMdata.y_size = struct.unpack('f', sm4_file[current_position:\
current_position+4])[0]
current_position += 4
globvar_AFMdata.z_factor.append(struct.unpack('f',
sm4_file[current_position:current_position+4])[0])
current_position += 4
# Offsets
current_position += 4
globvar_AFMdata.x_off = struct.unpack('f', sm4_file[current_position: \
current_position+4])[0]
current_position += 4
globvar_AFMdata.y_off = struct.unpack('f', sm4_file[current_position: \
current_position+4])[0]
current_position += 4
# Speed
current_position += 4
globvar_AFMdata.speed = struct.unpack('f', sm4_file[current_position: \
current_position+4])[0] * \
int(globvar_AFMdata.x_pixel) * 1000
current_position += 4
# Voltage
globvar_AFMdata.voltage = "{0:.1f}".format(struct.unpack('f',
sm4_file[current_position: \
current_position+4])[0])
current_position += 4
# Angle
current_position += 4
globvar_AFMdata.angle = "{0:.1f}".format(struct.unpack('f', sm4_file[ \
current_position:current_position+4])[0])
current_position += 4
# Page Header Object List
current_position += 12
page_header_object_list_count = struct.unpack('i', sm4_file \
[current_position:current_position+4])[0]
current_position += 4
# Go to the page header object list.
current_position = int(array_objects[0,1]) + int(array_objects[0,2])
array_header_objects = numpy.zeros((page_header_object_list_count, 3))
for i in range(page_header_object_list_count):
array_header_objects[i,0] = struct.unpack('i', \
sm4_file[current_position:current_position+4])[0]
current_position += 4
array_header_objects[i,1] = struct.unpack('i', \
sm4_file[current_position:current_position+4])[0]
current_position += 4
array_header_objects[i,2] = struct.unpack('i', \
sm4_file[current_position:current_position+4])[0]
current_position += 4
# Go to the text string to get the title.
current_position = int(array_header_objects[0,1])
str_length = 2*struct.unpack('h', sm4_file[current_position: \
current_position+2])[0]
current_position += 2
channel_name = sm4_file[current_position:current_position+str_length:2]
current_position += str_length
# __________________________________________________ Check the title
# Substitut all whitespaces with underscore.
channel_name = str(re.sub('\s', '_', channel_name.decode("utf-8")))
# Search all special characters and change the title.
if re.search('\W', channel_name):
channel_name_index = re.search('\W', channel_name).start()
channel_name = channel_name[:channel_name_index]
# If there are underscores at the end of the title, eliminate them.
Space = True
while Space:
if channel_name[-1] == "_":
channel_name = channel_name[:-1]
else:
Space = False
# Replace all underscores with a simple whitespace
channel_name = re.sub('_', ' ', channel_name)
# Add the channel name.
rename_channel(channel_name)
# Skip next 4 strings.
for i in range(4):
str_length = 2*struct.unpack('h', sm4_file[current_position: \
current_position+2])[0]
current_position += 2
current_position += str_length
# date
str_length = 2*struct.unpack('h', sm4_file[current_position: \
current_position+2])[0]
current_position += 2
globvar_AFMdata.date = sm4_file[current_position: \
current_position+str_length:2].decode("utf-8")
current_position += str_length
# time
str_length = 2*struct.unpack('h', sm4_file[current_position: \
current_position+2])[0]
current_position += 2
globvar_AFMdata.date += ", " + sm4_file[current_position: \
current_position+str_length:2].decode("utf-8")
current_position += str_length
# Units
str_length = 2*struct.unpack('h', sm4_file[current_position: \
current_position+2])[0]
current_position += 2
globvar_AFMdata.spec_x_unit.append(sm4_file[current_position: \
current_position+str_length:2].decode("utf-8"))
# Clemens Barth, 2016-01-15 ........................
#
# There is a problem with the unit for y! The following code
# needs to be revized.
current_position += str_length
str_length = 2*struct.unpack('h', sm4_file[current_position: \
current_position+2])[0]
current_position += 2
globvar_AFMdata.spec_y_unit.append(sm4_file[current_position: \
current_position+str_length:2].decode("utf-8"))
#print(globvar_AFMdata.spec_x_unit, globvar_AFMdata.spec_y_unit)
# ...................... until here
current_position += str_length
str_length = 2*struct.unpack('h', sm4_file[current_position: \
current_position+2])[0]
current_position += 2
globvar_AFMdata.unit.append(sm4_file[current_position: \
current_position+str_length:2].decode("utf-8"))
current_position += str_length
# Topography scale is in nm.
if globvar_AFMdata.channel[-1].find("Topography") != -1 and \
globvar_AFMdata.unit[-1].find("m") != -1:
globvar_AFMdata.z_factor[-1] = float(globvar_AFMdata.z_factor[-1] \
*math.pow(10,9))
globvar_AFMdata.unit[-1] = "nm"
# Labels
str_length = 2*struct.unpack('h', sm4_file[current_position: \
current_position+2])[0]
current_position += 2
globvar_AFMdata.spec_x_label.append(sm4_file[current_position: \
current_position+str_length:2].decode("utf-8"))
current_position += str_length
str_length = 2*struct.unpack('h', sm4_file[current_position: \
current_position+2])[0]
current_position += 2
globvar_AFMdata.spec_y_label.append(sm4_file[current_position: \
current_position+str_length:2].decode("utf-8"))
current_position += str_length
# _____________________________________________________ Prepare image
if channel%2 == 0:
direction = "forward"
elif channel%2 != 0:
direction = "backward"
if int(page_data_type) != 0:
prepare_RHK_spektra(sm4_file_path, SM4_file,
channel,
int(array_objects[1,1]))
elif int(page_type) in [16,38,39]:
if globvar_ThumbPara.spektra == 1:
prepare_RHK_spektra(sm4_file_path,
SM4_file, channel,
int(array_objects[1,1]))
else:
if proceed_with(channel,direction):
prepare_RHK_images(sm4_file_path, SM4_file,
channel,
int(array_objects[1,1]))
else:
continue
return True
# This methods reads out the measurement data and prepares it for the image.
def prepare_RHK_images(file_path, file_name, image_nr, data_off):
# If the thumb image exists continue with the next one ... .
# (Don't calculate a second time.)
if image_nr%2 == 0:
direction = "forward"
elif image_nr%2 != 0:
direction = "backward"
picture_file_name = globvar_AFMdata.channel[-1].replace(" ","_") + "_" + \
direction + "_" + file_name
# Add the specific number of the channel.
# Fill empty space with zeros.
if int(RHK_PositionCounter.position / 10) == 0:
picture_file_name = "000" + str(RHK_PositionCounter.position) + \
"_" + picture_file_name
RHK_PositionCounter.add()
elif int(RHK_PositionCounter.position / 10) < 10:
picture_file_name = "00" + str(RHK_PositionCounter.position) + \
"_" + picture_file_name
RHK_PositionCounter.add()
elif int(RHK_PositionCounter.position / 10) < 100:
picture_file_name = "0" + str(RHK_PositionCounter.position) + \
"_" + picture_file_name
RHK_PositionCounter.add()
else:
picture_file_name = str(RHK_PositionCounter.position) + \
"_" + picture_file_name
RHK_PositionCounter.add()
if globvar_ThumbPara.png:
image_path = os.path.join(globvar_AFMdir.thumbnail_directory,
picture_file_name) + ".png"
else:
image_path = os.path.join(globvar_AFMdir.thumbnail_directory,
picture_file_name) + ".jpeg"
if os.path.isfile(image_path):
return
# Format Information
size_x = int(globvar_AFMdata.x_pixel)
size_y = int(globvar_AFMdata.y_pixel)
globvar_AFMdata.speed = "{0:.1f}".format(globvar_AFMdata.speed)
globvar_AFMdata.x_size = int(globvar_AFMdata.x_pixel) * \
float(globvar_AFMdata.x_size)*math.pow(10,9)
globvar_AFMdata.y_size = int(globvar_AFMdata.y_pixel) * \
float(globvar_AFMdata.y_size)*math.pow(10,9)
mirror_x = False
mirror_y = False
if globvar_AFMdata.x_size < 0:
mirror_x = True
if globvar_AFMdata.y_size < 0:
mirror_y = True
globvar_AFMdata.x_size = "{0:.1f}".format(abs(globvar_AFMdata.x_size))
globvar_AFMdata.y_size = "{0:.1f}".format(abs(globvar_AFMdata.y_size))
globvar_AFMdata.x_off = "{0:.1f}".format(float(globvar_AFMdata.x_off) * \
math.pow(10,9))
globvar_AFMdata.y_off = "{0:.1f}".format(float(globvar_AFMdata.y_off) * \
math.pow(10,9))
# Read the position of the binary data.
with open(file_path, "rb") as sm4_file: # open file
# Go to position in the file.
sm4_file.seek(data_off,0)
# Go straight to numpy data (no buffering).
data = numpy.fromfile(sm4_file, dtype = numpy.int32,
count = size_x*size_y)
# Check if the image really has the expected size.
# 'size_x*size_y'.
# old_y_size and old_y_pixel are needed to temporarily store
# the original size in y (nm and pixel). This is then used
# at the end to put back the original values for
# globvar_AFMdata.y_size and globvar_AFMdata.y_pixel, for the
# text in the thumbs.
data, old_y_size, old_y_pixel = check_image_properties(data,
globvar_AFMdata.datfile[-1])
# If there is some corrupt image the y value, which has been
# changed in check_image_properties, needs to be updated.
size_y = int(globvar_AFMdata.y_pixel)
# First, transform the integers into floats. This is needed for all
# mathematical operations.
data = data.astype(float)
# Average value
average = numpy.average(data)
data = data * globvar_AFMdata.z_factor[-1]
# Calibrate the avergae value and write it into the globvar_AFMdata
# structure.
globvar_AFMdata.z_average = average * globvar_AFMdata.z_factor[-1]
# Make now a 2D array.
data.shape = numpy.array([size_y, size_x])
# Mirror the data
if mirror_x:
data = numpy.fliplr(data)
if not mirror_y:
data = numpy.flipud(data)
# Fitting
if line_fit(image_nr,direction):
data = linalg_line_fit(size_x, size_y, data)
if plane_fit(image_nr,direction):
data = linalg_plane_fit(size_x, size_y, data)
create_thumb_images(data, image_nr, picture_file_name, file_name)
if old_y_size != "" and old_y_pixel != "":
globvar_AFMdata.y_size = old_y_size
globvar_AFMdata.y_pixel = old_y_pixel
return True
def prepare_RHK_spektra(file_path, file_name, image_nr, data_off):
picture_file_name = globvar_AFMdata.channel[-1].replace(" ","_") + "_" + \
file_name
# Add the specific number of the channel.
# Fill empty space with zeros.
if int(RHK_PositionCounter.position / 10) == 0:
picture_file_name = "000" + str(RHK_PositionCounter.position) + \
"_" + picture_file_name
RHK_PositionCounter.add()
elif int(RHK_PositionCounter.position / 10) < 10:
picture_file_name = "00" + str(RHK_PositionCounter.position) + \
"_" + picture_file_name
RHK_PositionCounter.add()
elif int(RHK_PositionCounter.position / 10) < 100:
picture_file_name = "0" + str(RHK_PositionCounter.position) + \
"_" + picture_file_name
RHK_PositionCounter.add()
else:
picture_file_name = str(RHK_PositionCounter.position) + \
"_" + picture_file_name
RHK_PositionCounter.add()
if globvar_ThumbPara.png:
image_path = os.path.join(globvar_AFMdir.thumbnail_directory,
picture_file_name) + ".png"
else:
image_path = os.path.join(globvar_AFMdir.thumbnail_directory,
picture_file_name) + ".jpeg"
if os.path.isfile(image_path):
return
# Format Text
globvar_AFMdata.spec_points = [0]
globvar_AFMdata.spec_points[0] = globvar_AFMdata.x_pixel
globvar_AFMdata.spec_acquisition.append(
"{0:.6f}".format(globvar_AFMdata.speed / math.pow(10,6)))
globvar_AFMdata.spec_delay = [""]
globvar_AFMdata.spec_feedback = [""]
# Read the position of the binary data.
size_x = int(globvar_AFMdata.x_pixel)
size_y = int(globvar_AFMdata.y_pixel)
x_data_list = []
y_data_list = []
x_data = [(x*abs(float(globvar_AFMdata.x_size))) + \
float(globvar_AFMdata.x_off) for x in range(size_x)]
with open(file_path, "rb") as sm4_file: # open file
# Go to position in the file.
sm4_file.seek(data_off,0)
# Go straight to numpy data (no buffering).
for i in range(size_y):
x_data_list.append(x_data)
y_data = numpy.fromfile(sm4_file, dtype = numpy.int32,
count = size_x)
y_data = y_data.astype(float)
y_data = y_data*globvar_AFMdata.z_factor[-1]
y_data += globvar_AFMdata.y_off
y_data_list.append(y_data)
create_thumb_spectra(x_data_list,
y_data_list,
0,
picture_file_name,
image_path,
globvar_AFMdata.channel[-1])
return True