SkillAgentSearch skills...

Libasd

C++11/Python3 library to read/write High Speed AFM data file

Install / Use

/learn @ToruNiina/Libasd
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

libasd

Build Status release license DOI

A C++11 header-only library to read an .asd file generated by high-speed AFM, depending only on the C++ standard library.

libasd provides a binding for Python3 depending on pybind11.

logo

Table of Contents

Usage in Python3

Installation via pip

If you are ...

  • on Windows (x86_64), macOS (x86_64), or Linux (x86_64) and
  • using Python 3.6, 3.7, 3.8, 3.9, 3.10, or 3.11,

you can install it via pip.

$ pip install libasd

The complete list of supported environment can be found here

Otherwise, you need to build it by yourself (see the following section).

Building by yourself

It depends on pybind11. The build script uses CMake. Please make sure that you have installed cmake.

$ git clone --recursive https://github.com/ToruNiina/libasd.git
$ pip install --user ./libasd

Example Code

After installation, you can use the library in the following way.

import libasd
import numpy as np
import matplotlib.pyplot as plt

data = libasd.read_asd("example.asd")

print("version    = {}"     .format(data.header.file_version))
print("image size = {}x{}"  .format(data.header.x_pixel, data.header.y_pixel))
print("there are {} frames.".format(len(data.frames)))

plt.imshow(data.frames[0].image(), cmap="afmhot")
plt.show()

Unlike C++ version, Python version requires neither a version information nor a channel information. By reading the file, it extracts the information at a runtime, dynamically. C++ version requires both of them in order to determine types at a compile time, statically.

If the file contains several channels, then the data will have data.channels that is a list of frames.

print("version            = {}".format(data.header.file_version))
print("number of channels = {}".format(len(data.channels)))
print("type of channel1   = {}".format(data.header.data_type_1ch))
print("type of channel2   = {}".format(data.header.data_type_2ch))
print("number of frames: 1ch = {}, 2ch = {}".format(len(data.channels[0]), len(data.channels[1])))

# It plots 10-th frame in the first(0-th) channel.
plt.imshow(data.channels[0][10].image(), cmap="afmhot")
plt.show()

For more details, run help(data) on the python interpreter.

With NumPy

Because libasd aims the interoperability with NumPy, frame.image() function returns numpy.ndarray.

img = data.frames[0].image() # img is numpy.ndarray

libasd.Frame also has data object as a member that contains the same information as the return value of image. It uses Buffer protocol relying on pybind11 that enables users to cast a libasd.Frame object to NumPy Array without copying all the elements in the frame (don't forget to set copy = False).

import libasd
import numpy as np

# read the data
data = libasd.read_asd("example.asd")

# cast frame data into numpy Array
frame_0 = np.array(data.frames[0].data, copy = False)

To manipulate the frame datas, it is recommended to cast the data to the NumPy Arrays.

Miscellaneous

Python binding provides a method to read a header information only.

import libasd

header = libasd.read_header("example.asd")

print("version    = {}"   .format(header.file_version))
print("image size = {}x{}".format(header.x_pixel, header.y_pixel))

And it also supports reading raw data. read_asd returns frame data after converting the raw data to the height information, but read_raw_data returns frames without any conversion.

import libasd

raw_data = libasd.read_raw_data("example.asd")
raw_frame_0 = np.array(raw_data.frames[0].data, copy = False)
plt.imshow(raw_frame_0)
plt.show()

Usage in C++

libasd is a header-only library, so you don't need to build anything if you use it in your C++ code.

The only thing you have to do is adding path/to/libasd to your include path when you compile your code.

$ g++ -I/path/to/libasd -std=c++11 your_code.cpp

Then you can read the data in the way described below.

#include <libasd/libasd.hpp>
#include <fstream>
#include <iostream>

int main()
{
    std::ifstream ifs("example.asd");
    const auto data = asd::read_asd<double>(ifs);

    std::cout << "x_pixel = " << data.header.x_pixel << '\n';
    std::cout << "y_pixel = " << data.header.y_pixel << '\n';

    for(auto const& frame : data.frames)
    {
        for(auto const& line : frame)
        {
            for(auto const& pixel : line)
            {
                std::cout << pixel << ','; // height [nm] for topography, ...
            }
            std::cout << '\n';
        }
        std::cout << "\n\n";
    }
    std::cout << std::flush;
    return 0;
}

Here you can access each frame, line, and pixel intuitively by using range-based for loops.

For the N-channel data, you need one more loop.

#include <libasd/libasd.hpp>
#include <fstream>
#include <iostream>

int main()
{
    std::ifstream ifs("example.asd");
    const auto data = asd::read_asd<double, asd::ch<2>>(ifs);

    std::cout << "x_pixel = " << data.header.x_pixel << '\n';
    std::cout << "y_pixel = " << data.header.y_pixel << '\n';

    for(auto const& frames : data.channels)
    {
        for(auto const& frame : frames)
        {
            for(auto const& line : frame)
            {
                for(auto const& pixel : line)
                {
                    std::cout << pixel << ','; // height [nm] for topography, ...
                }
                std::cout << '\n';
            }
            std::cout << "\n\n";
        }
    }
    std::cout << std::flush;
    return 0;
}

You can set file version and channel as a template parameter.

const auto data = asd::read_asd<double, asd::ch<2>, asd::ver<0>>(ifs);

By default, these are set as channel 1, version 1 respectively.

You can access to each pixel in each line by index.

    const std::size_t x_pixel = data.header.x_pixel;
    const std::size_t y_pixel = data.header.y_pixel;
    for(const auto& frame : data.frames)
    {
        for(std::size_t y=0; y<y_pixel; ++y)
        {
            for(std::size_t x=0; x<x_pixel; ++x)
            {
                std::cout << frame[y][x]; // note: not [x][y].
            }
            std::cout << '\n';
        }
        std::cout << "\n\n";
    }

Be careful with the order of index. Frame[y] returns a Line at y. So first you must specify y value of the pixel.

FAQ

I need only file-header information. Frame data are not needed.

libasd provides asd::read_header function (libasd.read_header in python). It reads only file-header information.

#include <libasd/libasd.hpp>
#include <fstream>
#include <iostream>

int main()
{
    std::ifstream ifs("example.asd");
    const auto data = asd::read_header(ifs);
    // If you want to set file version information,
    // data = libasd.read_header<asd::ver<2>>("example.asd");
    // file version is set as asd::ver<1> by default.
    return 0;
}
import libasd

data = libasd.read_header("example.asd");
# If you want to set file version information,
# data = libasd.read_header("example.asd", version = 2);

It does not need the channel information because header format does not depend on the number of channels.

I don't want to use streams. What can I do?

You can pass a char const* to read_asd function in exactly the same way as iostreams.

How does it contain frame data?

It contains pixels in one frame data by one, contiguous container.

You might think that libasd contains a frame as an array of arrays because you can access to the pixel by using this form frame[y][x]. So you might be afraid of the performance loss that is owing to a cache-miss while you access to each line. But it is NOT true.

To make the usage easier, libasd provides a proxy class to access each lines. It wraps frame class and enable you to access a particular line by using container-like interface.

You can use raw std::vector<pixel_type>::(const_)iterator in the following way if you want.

const auto frame = data.frames.front();
for(auto iter = frame.raw_begin(), iend = frame.raw_end(); iter != iend; ++iter)
{
    std::cerr << *iter << ' '; // you can traverse all the pixel in one frame!
}

Can I use my awesome container with libasd?

If you implemented or found a container or an allocator that has a great feature, you may want to use it with libasd instead of std::vector<T, std::allocator<T>>.

In libasd, you can specify the container used to store pixel data by passing the special struct as a template parameter. NOTE: The container should be randomly accessible(by using operator[]). But `ContiguousContainer

View on GitHub
GitHub Stars14
CategoryDevelopment
Updated8mo ago
Forks8

Languages

C++

Security Score

87/100

Audited on Jul 16, 2025

No findings