I'm trying to deal with ECG signal processing in android. I want to implement simple digital filters (lowpass, highpass)
I've got a transfer function:
here is what i've found:
wikipedia - lowpass filter - it looks quite easy here.
for i from 1 to n
y[i] := y[i-1] + α * (x[i] - y[i-1])
but there is nothing about transfer function which I want to use.
I also found the following matlab code
%% Low Pass Filter H(z) = (1 - 2z^(-6) + z^(-12)) / (1 - 2z^(-1) + z^(-2))
b = [1 0 0 0 0 0 -2 0 0 0 0 0 1];
a = [1 -2 1];
h_l = filter(b,a,[1 zeros(1,12)]);
ecg_l = conv (ecg ,h_l);
but there is no function like filter and conv in java (or I missed something).
Also I was looking on stackoverflow for an answer. But I didn't found anything about transfer function.
so can someone help me? I just want to move on with my project.
Given a time-domain recurrence equation (such as the one you quoted from wikipedia), the corresponding transfer function in the z-domain can relatively easily be obtained by using the following properties:
Where X(z) and Y(z) are the z-transforms of the time-domain input sequence x and output sequence y respectively.
Going the other way around, given a transfer function which can be expressed as a ratio of polynomials in z, such as:
the recurrence equation of the transfer function can be written as:
There are of course many different ways to implement such a recurrence equation, but a simple filter implementation following the Direct Form II would be along the line of:
// Implementation of an Infinite Impulse Response (IIR) filter
// with recurrence equation:
// y[n] = -\sum_{i=1}^M a_i y[n-i] + \sum_{i=0}^N b_i x[n-i]
public class IIRFilter {
public IIRFilter(float a_[], float b_[]) {
// initialize memory elements
int N = Math.max(a_.length, b_.length);
memory = new float[N-1];
for (int i = 0; i < memory.length; i++) {
memory[i] = 0.0f;
}
// copy filter coefficients
a = new float[N];
int i = 0;
for (; i < a_.length; i++) {
a[i] = a_[i];
}
for (; i < N; i++) {
a[i] = 0.0f;
}
b = new float[N];
i = 0;
for (; i < b_.length; i++) {
b[i] = b_[i];
}
for (; i < N; i++) {
b[i] = 0.0f;
}
}
// Filter samples from input buffer, and store result in output buffer.
// Implementation based on Direct Form II.
// Works similar to matlab's "output = filter(b,a,input)" command
public void process(float input[], float output[]) {
for (int i = 0; i < input.length; i++) {
float in = input[i];
float out = 0.0f;
for (int j = memory.length-1; j >= 0; j--) {
in -= a[j+1] * memory[j];
out += b[j+1] * memory[j];
}
out += b[0] * in;
output[i] = out;
// shift memory
for (int j = memory.length-1; j > 0; j--) {
memory[j] = memory[j - 1];
}
memory[0] = in;
}
}
private float[] a;
private float[] b;
private float[] memory;
}
which you could use to implement your specific transfer function like so:
float g = 1.0f/32.0f; // overall filter gain
float[] a = {1, -2, 1};
float[] b = {g, 0, 0, 0, 0, 0, -2*g, 0, 0, 0, 0, 0, g};
IIRFilter filter = new IIRFilter(a, b);
filter.process(input, output);
Note that you can alternatively also factorize the numerator and denominator into 2nd order polynomials and obtain a cascade of 2nd order filters (known as biquad filters).
Related
I am trying to port a tensorflow model to tensorflow lite to use it in an android application. The conversion is successful and everything runs except for Internal error: Failed to run on the given Interpreter: input must be 5-dimensional. The input in the original model was input_shape=(20, 320, 240, 1), which is 20 320 x 240 grayscale images (therefore ...,1). Here is the important code:
List<Mat> preprocessedFrames = preprocFrames(buf);
//has length of 20 -> no problem there (shouldn't affect dimensionality either...)
int[] output = new int[2];
float[][][] inputMatrices = new float[preprocessedFrames.toArray().length][320][240];
for(int i = 0; i < preprocessedFrames.toArray().length; i++) {
Mat inpRaw = preprocessedFrames.get(i);
Bitmap data = Bitmap.createBitmap(inpRaw.cols(), inpRaw.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(inpRaw, data);
int[][] pixels = pixelsFromBitmap(data);
float[][] inputMatrix = inputMatrixFromIntPixels(pixels);
// returns float[][] with floats from 0 to 1
inputMatrices[i] = inputMatrix;
}
try{
detector.run(inputMatrices, output);
Debug("results: " + output.toString());
}
The model gives me an output of 2 neurons translating into 2 labels.
The model code is the following:
model = tf.keras.Sequential(name='detector')
model.add(tf.keras.layers.Conv3D(filters=(56), input_shape=(20, 320, 240, 1), strides=(2,2,2), kernel_size=(3,11,11), padding='same', activation="relu"))
model.add(tf.keras.layers.AveragePooling3D(pool_size=(1,4,4)))
model.add(tf.keras.layers.Conv3D(filters=(72), kernel_size=(4,7,7), strides=(1,2,2), padding='same'))
model.add(tf.keras.layers.Conv3D(filters=(81), kernel_size=(2,4,4), strides=(2,2,2), padding='same'))
model.add(tf.keras.layers.Conv3D(filters=(100), kernel_size=(1,2,2), strides=(3,2,2), padding='same'))
model.add(tf.keras.layers.Conv3D(filters=(128), kernel_size=(1,2,2), padding='same'))
model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dense(768, activation='tanh', kernel_regularizer=tf.keras.regularizers.l2(0.011)))
model.add(tf.keras.layers.Dropout(rate=0.1))
model.add(tf.keras.layers.Dense(256, activation='sigmoid', kernel_regularizer=tf.keras.regularizers.l2(0.012)))
model.add(tf.keras.layers.Dense(2, activation='softmax'))
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.00001), loss=tf.keras.losses.CategoricalCrossentropy(),
metrics=['accuracy'])
EDIT: I printed out the first input tensor as follows:
int[] shape = detector.getInputTensor(0).shape();
for(int r = 0; r < shape.length; r++){
Log.d("********" + r, "*******: " + r + " : " + shape[r]);
}
With that I first get the output [1,20,320,240,1]and after that I only get [20,320,240]. I am really quite desperate now...
So, I figured it out by myself and it seems like I really only had to make the input 5 dimensional by putting the content into a first dimension and every single pixel into a fifth dimension. I don't know why, but I will accept that xD.
float[][] output = new float[1][2];
float[][][][][] inputMatrices = new float[1][preprocessedFrames.toArray().length][320][240][1];
for(int i = 0; i < preprocessedFrames.toArray().length; i++) {
Mat inpRaw = preprocessedFrames.get(i);
Bitmap data = Bitmap.createBitmap(inpRaw.cols(), inpRaw.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(inpRaw, data);
int[][] pixels = pixelsFromBitmap(data);
float[][] inputMatrix = inputMatrixFromIntPixels(pixels);
for (int j = 0; j < inputMatrix.length - 1; j++) {
for(int k = 0; k < inputMatrix[0].length - 1; k++) {
inputMatrices[0][i][k][j][0] = inputMatrix[j][k];
}
}
}
I use the following procedure for transferring an array of float numbers to a RenderScript Kernel and it works fine.
float[] w = new float[10];
Allocation w_rs = Allocation.createSized(rs, Element.F32(rs), 10);
w_rs.copy1DRangeFrom(0, 10, w);
I want to use a similar procedure for transferring Float4 values as follows
Float4[] w = new Float4[10];
for (int i = 0; i < 10; i++) {
w[i] = new Float4(i, 2*i, 3*i, 4*i);
}
Allocation w_rs = Allocation.createSized(rs, Element.F32_4(rs), 10);
w_rs.copy1DRangeFromUnchecked(0, 10, w);
Which results in the following error
Object passed is not an Array of primitives
Apparently, w should be array of primitives. But I want w to be array of Float4.
You can simply use:
float[] w = new float[4 * 10];
for (int i = 0; i < 10; i++) {
w[i * 4 + 0] = i;
w[i * 4 + 1] = i*2;
w[i * 4 + 2] = i*3;
w[i * 4 + 3] = i*4;
}
Allocation w_rs = Allocation.createSized(rs, Element.F32_4(rs), 10);
w_rs.copyFrom(w);
// Or
w_rs.copy1DRangeFrom(0,40,w);
Painless :)
Reference: RenderScript: parallel computing on Android, the easy way
Deeper explanation
Inside RenderScript Java source code, you'll see this middleware function:
public void copy1DRangeFromUnchecked(int off, int count, Object array) {
copy1DRangeFromUnchecked(off, count, array,
validateObjectIsPrimitiveArray(array, false),
java.lang.reflect.Array.getLength(array));
}
The validateObjectIsPrimitiveArray is performed on any copy-method invocation. You can pass only raw arrays.
I'm working on an audio based application for android platform.in this code first i record my voice in wav format and then i get fft(in fftbegir method) and now i want to filter my voice in 0-4khz with zeroing fft data and then perform ifft but when i play new wav file i can hear very bad quality sound with lots of noise .the fft class is here http://introcs.cs.princeton.edu/java/97data/FFT.java.html and here is my code:
private void filter(Complex[] x, int size) throws IOException {
double d, b;
String strI;
byte[] bytes = new byte[2];
int i = 0;
double k = -3.14159;
Complex[] f = new Complex[size];
Complex[] iff;
byte[] ddd;
double[] kkk = new double[size];
FFT q = new FFT();
short shor;
double data9[] = new double[size];
d = 2 * 3.14159 / size;
totalAudioLen = size;
totalDataLen = totalAudioLen + 36;
while (i < size) {//////to make its lenght length power of 2
data9[i] = k;
k = k + d;
i++;
}
i = 0;
while (i < (size / 2) - 2000) {
f[i] = new Complex(x[i].re(), x[i].im());
i++;
}
while (i < (size / 2) + 2000) { ///i want to remov 2000 sample of fft
f[i] = new Complex(0, 0);
i++;
}
while (i < size) {
f[i] = new Complex(x[i].re(), x[i].im());
i++;
}
iff = q.ifft(f);
try {
out9 = new FileOutputStream(getridemal());
out10 = new FileOutputStream(getwavfilter());
out11 = new FileOutputStream(getkhodesh());
WriteWaveFileHeader(out10, totalAudioLen, totalDataLen,
longSampleRate, channels, byteRate);
for (i = 0; i < size; i++) {
b = iff[i].re();
shor = (short) (b * 32768.0);
bytes = ByteConvert.convertToByteArray(shor);
out10.write(bytes, 0, 2);
}
} finally {
out9.close();
out10.close();
out11.close();
}
}
private void fftbegir(String input, String output) {
double[] data8;
int i = 0;
int r, k, l;
double b;
int m = 2;
try {
in5 = new FileInputStream(input);
out5 = new FileOutputStream(output);
AppLog.logString("File size: " + totalDataLen);
totalAudioLen = in5.getChannel().size();
data8 = SoundDataUtils.load16BitPCMRawDataFileAsDoubleArray();
l = data8.length;
while (l > m) {
m = m * 2;
}
Complex[] x = new Complex[m];
while (i < l) {
x[i] = new Complex(data8[i], 0);
i++;
}
in5.close();
i--;
for (i = l; i < m; i++) {
x[i] = new Complex(0, 0);
}
FFT f = new FFT();
Complex[] y = f.fft(x);
filter(y, m);
out5.close();
}
}
thanks:)
Filtering in the frequency domain as you are doing does not work well.
Whilst applying the inverse FFT to the results of a FFT yields the same samples (that is to say, it is invertible), this no longer holds true when coefficients are modified.
There are (at least) a few issues here:
The Gibbs Phenomemum which results from the sharp transition from pass-band to stop-band
The fact that the FFT is a fairly lousy band-pass filter in the first place. Components of the frequencies in the stop-band appear in several adjacent bands, and therefore remain in the signal.
Each FFT bin contains a real and imaginary component. A complex value of (0,0) has magnitude of 0 but also loses the phase information in the process.
You'd be better off with an IIR band-stop filter, which operates in the time domain. Besides working as expected, it is far cheaper to compute too.
Hello i want to convert the color in image, i'm using per-pixel methods but it seems very slow
src.getPixels(pixels, 0, width, 0, 0, width, height);
// RGB values
int R;
for (int i = 0; i < pixels.length; i++) {
// Get RGB values as ints
// Set pixel color
pixels[i] = color;
}
// Set pixels
src.setPixels(pixels, 0, width, 0, 0, width, height);
my question, is there any way i can do it using openCV? change pixel to the color i want ?
I recommend this excellent article on how to access/modify an opencv image buffer. I recommend
"the efficient way":
int i,j;
uchar* p;
for( i = 0; i < nRows; ++i)
{
p = I.ptr<uchar>(i);
for ( j = 0; j < nCols; ++j)
{
p[j] = table[p[j]];
}
Or "the iterator-safe method":
MatIterator_<Vec3b> it, end;
for( it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it)
{
(*it)[0] = table[(*it)[0]];
(*it)[1] = table[(*it)[1]];
(*it)[2] = table[(*it)[2]];
}
For further optimizations, using cv::LUT() (where possible) can give huge speedups, but it is more intensive to design/code.
You can access Pixels by using:
img.at<Type>(y, x);
So to change an RGB Value you can use:
// read color
Vec3b intensity = img.at<Vec3b>(y, x);
// compute new color using intensity.val[0] etc. to access color values
// write new color
img.at<Vec3b>(y, x) = intensity;
#Boyko mentioned an Article from OpenCV concerning fast access to the image pixels if you want to iterate over all Pixel. The Method I would prefer from this Article is the iterator Method, as it is only slightly slower than direct pointer access but safer to use.
Example Code:
Mat& AssignNewColors(Mat& img)
{
// accept only char type matrices
CV_Assert(img.depth() != sizeof(uchar));
const int channels = img.channels();
switch(channels)
{
// case 1: skipped here
case 3:
{
// Read RGG Pixels
Mat_<Vec3b> _img = img;
for( int i = 0; i < img.rows; ++i)
for( int j = 0; j < img.cols; ++j )
{
_img(i,j)[0] = computeNewColor(_img(i,j)[0]);
_img(i,j)[1] = computeNewColor(_img(i,j)[1]);
_img(i,j)[2] = computeNewColor(_img(i,j)[2]);
}
img = _img;
break;
}
}
return img;
}
I'm using this opencv code on Android for an intensity equalization. The time of execution is around 300ms per frame (720x480). Does anybody have an idea for a possible time optimization ?
Here is the code :
cvtColor(image, hsvImage, CV_BGR2HSV);
// Get intensity
intensity = hsvImage.at<Vec3b>((int)reference.Point_::y, (int)reference.Point_::x);
float value = (float)REGULAR_INTENSITY / intensity[2];
float saturation = (float)REGULAR_SATURATION / intensity[1];
if (counter == 15 && (int)intensity[2] < REGULAR_INTENSITY) {
equalization = false;
}
// Modify intensity
float transformedSaturation, transformedValue;
for(int i = 0; i < hsvImage.rows; i++) {
unsigned char *data = hsvImage.ptr(i);
for(int j = 0; j < hsvImage.cols; j++) {
transformedSaturation = (uchar)*++data * saturation;
if (transformedSaturation > MAX_COLOR) {
transformedSaturation = MAX_COLOR;
}
*data++ = transformedSaturation;
transformedValue = (uchar)*data * value;
if (transformedValue > MAX_COLOR) {
transformedValue = MAX_COLOR;
}
*data++ = transformedValue;
}
}
cvtColor(hsvImage, image, CV_HSV2BGR);
Have you tried using OpenCV transform ? I guess it is optimised, but I dont know about the saturating cast (that is if > max_color, then = max color)