Git Product home page Git Product logo

savitzky-golay's Introduction

Savitzky-Golay Filter in C++

Author: Lekan Ogunmolu

Table of Contents

Introduction

Nicely computes the Vandermonde matrix, Savitzky-Golay differentiation filters, and smoothing coefficients for any sequential signal. It is a textbook implementation of the Savitzky-Golay Filter. Initial testing of this code was on a Ubuntu 14.04.02 Trusty OS running Linux 4.4 but will work on any other Linux/Windows/Mac OS machine with little effort.

Below are examples of how the filter smoothes out a noisy depth map data from the kinect time-of-flight sensor:

Dependencies

  • Eigen3 Library for the linear algebra of the matrices, vectors and related algorithms. You can download the 3.2.5 library which I used from here and follow the README instructions after unpacking the tarball to install.

Usage

  • ./savgol

    Options

    • -h or --help: print out the help menu.

    • Example: Compute the savitzky-golay filter coefficients with frame size, F = 5 and polynomial order 3 (these are the default parameters of the filter) for linearly spaced data points between x_min = 900 and x_max = 980. A typical cmd line usage:

      ./savgoal 9 5 where F = 9 and k = 5.

    • To pass in your arbitrary data points between a value x_min and x_max, run this way in order: ./savgoal F k x_min x_max.

    The filtered values are returned to the console. Note that the Frame size should ideally be odd.

Components

  • MatrixXi vander(const int F);

    • Computes the Vandermonde matrix and the polynomial of basis vectors; it flips the vector column-wise, left to right.
  • MatrixXf B = MatrixXf sgdiff(int k, double F)

    • Designs a Savitzky-Golay FIR smoothing filter, B, with polynomial order k and frame size F of convolution coefficients. The polynomial order, k, must be less than the frame size F, and F must be odd.
  • savgolfilt(x, x_on, k, F)

    • Computes the smoothed values of the signal x, whose tansient on is x_on initialized with size F.
  • Note In calculating the transient off, x_off will be the last (F-1) x values, where x's are the data sequence we want to filter.If you are smoothing data offline, then this code will work seamlessly. Just load your data in the main() function where, for an example, I have used linearly spaced values between 900 and 980 at a frame 5 size for my steady state values.

Note, if you are smoothing data in real time, you need to find a way to let your compiler pick the last F-length samples from your data in order to compute your transient off, i.e., x_off. You could have the program wait for x_milliseconds after stopping your code before you pick the transient off, for example.

Compilation

There is a CMakeLists.txt file in the project root folder. From the project root directory:

  1. Create a build directory: mkdir build && cd build
  2. Compile the cpp code: cmake ../
  3. Build your executable: make
  4. Run the executable: ./savgol

Citation

If you have used Savitzky-Golay in your work, please cite it.

@misc{Savitzky-Golay,
  author = {Ogunmolu, Olalekan},
  title = {{Savitzky-Golay Filter in C++}},
  year = {2015},
  howpublished = {\url{https://github.com/lakehanne/Savitzky-Golay}},
  note = {Accessed August 15, 2015}
}

Issues

If you have issues running the files, please use the issues tab to open a bug. I will generally respond within a 24-hour period.

Changelog

  • Added citation to README (August 14, 2015)
  • Added examples to int main() function (August 15, 2015)
  • Modified frame size and polynomial order to be reconfigurable at run time (July 1, 2016)

TODO

Add a plotter to plot the filtered values on a gtk chart?

Reference

INTRODUCTION TO SIGNAL PROCESSING

Sophocles J. Orfanidis, Prentice Hall, 2010

Chapter 8; Section 8.3.5

savitzky-golay's People

Contributors

robotsorcerer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

savitzky-golay's Issues

Wrong matrix dimensions in function savgolfilt

In my case savgolfilt aborts at line

https://github.com/lakehanne/Savitzky-Golay/blob/099ad9fd268c97443842e06e0624232c21bfe66c/savgol.cpp#L140

with:

/usr/include/eigen3/Eigen/src/Core/CwiseBinaryOp.h:131: Eigen::CwiseBinaryOp<BinaryOp, Lhs, Rhs>::CwiseBinaryOp(const Lhs&, const Rhs&, const BinaryOp&) [with BinaryOp = Eigen::internal::scalar_product_op<double, double>; Lhs = const Eigen::Transpose<const Eigen::Matrix<double, 1, -1> >; Rhs = const Eigen::Matrix<double, -1, 1>]: assertion »aLhs.rows() == aRhs.rows() && aLhs.cols() == aRhs.cols() failed

My tested arguments for savgolfilt:
x: a vector of length 1650 and 1030
x_on: vector of length F initialized with the first F elements of x
k: 3 and 5
F: 11
Fd: 11

My version of Eigen: 3.2.0

OS: Ubuntu 14.04

unable to run input array size of 1000 and order 3

Dear Lekan,

I was trying to smooth a signal of size 1000 samples with order 3.
But when I execute I get output array size as polynomial order size.But output array should be the same size as the input array.
Could you help on same?

Thanks,
Amaresh P Kandagal

A Assert Error when running in Windows System

I transfer it to Windows Enviroment, I get a Error about Eigen Lib.

lhs.cols() == rhs.rows() && "invalid matrix product" && "if you wanted a coeff-wise or a dot product use the respective explicit functions", file E:\SavitzkyGolay\SavitzkyGolay\eigen\Eigen\src\Core\Product.h, line 98

How to solve it???
please help me...

vector input for savgolfilt() , input vector size 990, wrong output , result compared with python and matlab

x: a vector of length 990
I mean x is a vector of [x_1, x_2, ...., x_990]

FILE *fp_y;
fp_y=fopen("niosy_data.txt","r");
std::vector x_v(990);
if (fp_y == NULL)
cout<<"\n\nSpecified file is not found\n\n"<<endl;
int i = 0; float Y_data = 0;

while ((fscanf(fp_y, "%f", &Y_data)) != EOF)
{
x_v.push_back (Y_data);
i++;
}
fclose(fp_y);
Eigen::MapEigen::VectorXf x(x_v.data(), 990);
auto result_out = savgolfilt(x,3, 11);
cout<<result_out::<<endl;

result_out :: 0 0 0 0 0 -nan 0 0 0 0 0

Could you help on same

Matrix dimension mismatch

Hi ! I dowloaded your implementation of SG filter and did not succeed to run it as is. The assertion comes at the line MatrixXf G = Sd * Rinv * RinvT; in the function sgdiff. With F=5 and k=3, it seems that the dimensions of Sd is 5x4 but the dimensions of Rinv and RinvT is 1x1. Hence, the multiplication Sd * Rinv * RinvT cause the program to abord. I did not edited anything, just downloaded the library, created a blank project in VS 2015, imported those plus the Eigen library. Any idea ?

Unstability/misunderstanding of savgolfilt

Hi.

I downloaded your code and did some test runs. However, I encountered some issues.

I need to use the code within the bigger project. I have a .dll library where I need to apply s-g filtering. Although the main file is .cu, I don't use any Cuda code now. This library is then called from MFC application to run some operations on data.
I am working with Visual Studio 2019, Eigen 3.3, on Windows 10.

Issue 1

This code is pretty much copied from example.cpp. I run the function "savgolfilt" with a given set of values 30 times and sometimes, I get different result from "Filter". I use F, x, x values from example.

My code:

// initialize values
	int F = 9;      
	int k = 5; 
	float x_min = 100, x_max = 1000;

// create vector of x values
	auto x = VectorXf::LinSpaced(F, x_min, x_max);

// calculate the same Filter 30 times
	for (int i = 0; i < 30; i++)
	{
		auto Filter = savgolfilt(x, k, F);
		log_file << "Filter =  " << Filter << "\n"; // save to a log_file, which is later saved as .txt
	}

Results are as follows: middle value is different sometimes.

Filter = 662.501 775 887.5 1000 550 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 550 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 1.96313e+06 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 957289 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 957289 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 550 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 1.96313e+06 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 1.96313e+06 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 550 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 1.96313e+06 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 957289 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 957289 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 1.96313e+06 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 957289 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 957289 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 957289 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 550 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 1.96313e+06 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 1.96313e+06 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 957289 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 1.96313e+06 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 550 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 1.96313e+06 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 550 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 550 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 550 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 550 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 1.96313e+06 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 1.96313e+06 99.9998 212.5 325 437.5
Filter = 662.501 775 887.5 1000 957289 99.9998 212.5 325 437.5

I run a couple of tests to see where the error happens. It seems that the line
y << y_off.transpose().eval(), y_ss, y_on.transpose().eval();
in savgolfilt function is problematic - the vectors before concatenating into y seem just fine. Do you know what could be a problem?

Issue 2

I apply savgolfilt on my data. Typically, F = 129, k = 6, x is signal of length 400 (in other applications the length can be up to 1600) with float values in range for example (-0.0038, 0.0266). Here, I show example for F = 9 and k = 5 (in order not to show so many values).

		// initialize values
		int F = 9;
		int k = 5;

		// create vector of x values
		auto x = my_data;

		// calculate
		for (int i = 0; i < 10; i++)
		{
			auto Filter = savgolfilt(x, k, F);
			log_file << "Filter =  " << Filter << "\n";
		}

Result

Filter = 0.0066643 0.00634263 0.00642954 0.00900544 -nan 0.00869701 0.00574144 0.00707652 0.00823701
Filter = 0.0066643 0.00634263 0.00642954 0.00900544 -nan 0.00869701 0.00574144 0.00707652 0.00823701
Filter = 0.0066643 0.00634263 0.00642954 0.00900544 -nan 0.00869701 0.00574144 0.00707652 0.00823701
Filter = 0.0066643 0.00634263 0.00642954 0.00900544 -nan 0.00869701 0.00574144 0.00707652 0.00823701
Filter = 0.0066643 0.00634263 0.00642954 0.00900544 -nan 0.00869701 0.00574144 0.00707652 0.00823701
Filter = 0.0066643 0.00634263 0.00642954 0.00900544 -nan 0.00869701 0.00574144 0.00707652 0.00823701
Filter = 0.0066643 0.00634263 0.00642954 0.00900544 -nan 0.00869701 0.00574144 0.00707652 0.00823701
Filter = 0.0066643 0.00634263 0.00642954 0.00900544 -nan 0.00869701 0.00574144 0.00707652 0.00823701
Filter = 0.0066643 0.00634263 0.00642954 0.00900544 -nan 0.00869701 0.00574144 0.00707652 0.00823701
Filter = 0.0066643 0.00634263 0.00642954 0.00900544 -nan 0.00869701 0.00574144 0.00707652 0.00823701

There is nan in the middle. Is there any condition for data? Like they should be positive, in some range, or?
Or, is there just problem with << operator, which displays values of Eigen variables?

Issue/question 3

I am sorry, but I got confused, even more after reading other issues here. How exactly should I use the code to get smoothed signal? I expect the smoothed signal will have the same length as input signal x, but it is not the case of savgolfilt. In Readme.md there is "savgolfilt(x, x_on, k, F);", but I don't see the four-argument input of this function nor in savgol.cpp, nor in its header.

Thank you for your answers. I am sorry if I ask something trivial, but I didn't find answer anywhere.

./savgol do not work properly, please see comment

db@zweistein:/array_data01> git clone https://github.com/lakehanne/Savitzky-Golay.git
Klone nach 'Savitzky-Golay' ...
remote: Enumerating objects: 220, done.
remote: Total 220 (delta 0), reused 0 (delta 0), pack-reused 220
Empfange Objekte: 100% (220/220), 329.79 KiB | 0 bytes/s, Fertig.
Löse Unterschiede auf: 100% (123/123), Fertig.

db@zweistein:/array_data01> cd Savitzky-Golay
db@zweistein:/array_data01/Savitzky-Golay> mkdir build
db@zweistein:/array_data01/Savitzky-Golay> cd build/
db@zweistein:/array_data01/Savitzky-Golay/build> cmake ../
-- The C compiler identification is GNU 4.8.5
-- The CXX compiler identification is GNU 4.8.5
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found PkgConfig: /usr/bin/pkg-config (found version "0.28")
-- Checking for module 'eigen3'
-- Found eigen3, version 3.2.9
-- Performing Test COMPILER_SUPPORTS_CXX11
-- Performing Test COMPILER_SUPPORTS_CXX11 - Success
-- Performing Test COMPILER_SUPPORTS_CXX0X
-- Performing Test COMPILER_SUPPORTS_CXX0X - Success
-- savgol include path:

-- /array_data01/Savitzky-Golay/include

eigen3 include path: /usr/include/eigen3

-- Configuring done
-- Generating done
-- Build files have been written to: /array_data01/Savitzky-Golay/build
db@zweistein:/array_data01/Savitzky-Golay/build> make
Scanning dependencies of target savgol
[ 33%] Building CXX object CMakeFiles/savgol.dir/savgol.cpp.o
[ 66%] Building CXX object CMakeFiles/savgol.dir/example.cpp.o
[100%] Linking CXX executable savgol
[100%] Built target savgol

db@zweistein:/array_data01/Savitzky-Golay/build> ./savgol
Frame size: 5; Polynomial order: 3

Vandermonde Matrix:
1 -2 4 -8 16
1 -1 1 -1 1
1 0 0 0 0
1 1 1 1 1
1 2 4 8 16

Filtered values in the range
900 920 940 960 980
are:
960 980 940 900 920

db@zweistein:/array_data01/Savitzky-Golay/build> ./savgol --help

USAGE:

./savgol [<frame_size> [<polynomial_order> [<x_low> <x_high>] ] ]

   <frame_size>: odd int and ideally greater than
   <polynomial_order>:  an integer

   <x_low>, <x_high>: min and max limits of
   linspaced vector to be filtered.

===================================================
Example: ./savgol 9 5

db@zweistein:/array_data01/Savitzky-Golay/build> ./savgol 9 5

USAGE:

./savgol [<frame_size> [<polynomial_order> [<x_low> <x_high>] ] ]

   <frame_size>: odd int and ideally greater than
   <polynomial_order>:  an integer

   <x_low>, <x_high>: min and max limits of
   linspaced vector to be filtered.

===================================================
Example: ./savgol 9 5

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.