Skip to content

Instantly share code, notes, and snippets.

@carlodri
Last active July 16, 2024 11:13
Show Gist options
  • Select an option

  • Save carlodri/66c471498e6b52caf213 to your computer and use it in GitHub Desktop.

Select an option

Save carlodri/66c471498e6b52caf213 to your computer and use it in GitHub Desktop.
Simple Python I/O functions for the Gwyddion Simple Field (.gsf) format
def gsf_read(file_name):
'''Read a Gwyddion Simple Field 1.0 file format
http://gwyddion.net/documentation/user-guide-en/gsf.html
Args:
file_name (string): the name of the output (any extension will be replaced)
Returns:
metadata (dict): additional metadata to be included in the file
data (2darray): an arbitrary sized 2D array of arbitrary numeric type
'''
if file_name.rpartition('.')[1] == '.':
file_name = file_name[0:file_name.rfind('.')]
gsfFile = open(file_name + '.gsf', 'rb')
metadata = {}
# check if header is OK
if not(gsfFile.readline().decode('UTF-8') == 'Gwyddion Simple Field 1.0\n'):
gsfFile.close()
raise ValueError('File has wrong header')
term = b'00'
# read metadata header
while term != b'\x00':
line_string = gsfFile.readline().decode('UTF-8')
metadata[line_string.rpartition(' = ')[0]] = line_string.rpartition('=')[2]
term = gsfFile.read(1)
gsfFile.seek(-1, 1)
gsfFile.read(4 - gsfFile.tell() % 4)
#fix known metadata types from .gsf file specs
#first the mandatory ones...
metadata['XRes'] = np.int(metadata['XRes'])
metadata['YRes'] = np.int(metadata['YRes'])
#now check for the optional ones
if 'XReal' in metadata:
metadata['XReal'] = np.float(metadata['XReal'])
if 'YReal' in metadata:
metadata['YReal'] = np.float(metadata['YReal'])
if 'XOffset' in metadata:
metadata['XOffset'] = np.float(metadata['XOffset'])
if 'YOffset' in metadata:
metadata['YOffset'] = np.float(metadata['YOffset'])
data = np.frombuffer(gsfFile.read(),dtype='float32').reshape(metadata['YRes'],metadata['XRes'])
gsfFile.close()
return metadata, data
def gsf_write(data, file_name, metadata={}):
'''Write a 2D array to a Gwyddion Simple Field 1.0 file format
http://gwyddion.net/documentation/user-guide-en/gsf.html
Args:
file_name (string): the name of the output (any extension will be replaced)
data (2darray): an arbitrary sized 2D array of arbitrary numeric type
metadata (dict): additional metadata to be included in the file
Returns:
nothing
'''
XRes = data.shape[0]
YRes = data.shape[1]
data = data.astype('float32')
if file_name.rpartition('.')[1] == '.':
file_name = file_name[0:file_name.rfind('.')]
gsfFile = open(file_name + '.gsf', 'wb')
s = ''
s += 'Gwyddion Simple Field 1.0' + '\n'
s += 'XRes = {0:d}'.format(XRes) + '\n'
s += 'YRes = {0:d}'.format(YRes) + '\n'
for i in metadata.keys():
try:
s += i + ' = ' + '{0:G}'.format(metadata[i]) + '\n'
except:
s += i + ' = ' + str(metadata[i]) + '\n'
gsfFile.write(bytes(s, 'UTF-8'))
gsfFile.write(b'\x00' * (4 - len(s) % 4))
gsfFile.write(data.tobytes(None))
gsfFile.close()
@carlodri
Copy link
Author

To everyone interested in this: the tool has been published as a Python package (gsffile) thanks to the great review and rework of my dear friend @angelo-peronio! 🚀

It is available on PyPI and very soon on conda-forge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment