Медіанний фільтр (Median Filter)
Додано: 24 січня 2021, 20:16
У проекті IB металошукача на ARDUINO DUE для фільтрації значення VDI було використано медіанний фільтр (Filter size > 3), але він не коректно працює із "піками" з іншим знаком ніж основний сигнал.
pawa запропонував свій варіант медіанного фільтра:
При адаптації до ARDUINO, зіткнувся з тим, що відсутня функція window.Length котра дає нам кількість елементів масиву (хоча для даного завдання довжину масиву краще задати змінною), тому для збереження універсальності було використано оператор sizeof (window), котрий дає нам довжину масиву в байтах. Для того, щоб отримати розмір масиву, необхідно розділити довжину масиву на кількість байтів одного елемента (так як у нас тип масиву "int", довжина одного елемента 4 байти).
Наступною перепоною було те, що у функцію не можна передати масив, а лише посилання на нього. Таким чином функція працює, і модифікує (!), з основним масивом. Для уникнення змін в основному масиві, прийшлось зробити його копіювання в допоміжний вже у самій функції.
В результаті отримано наступний коод медіанного фільтра:
PS. Даний код потребує вдосконалення. Виконується на ARDUINO DUE за 9 мікросекунд (без копіювання масиву - за 3). Можна його протестувати з іншими алгоритмами сортування. Копіювання масиву значно сповільнює роботу - 1 мікросекунда на кожен елемент; кращим був би алгоритм з масивом в самій функції і надсиланням до неї лише нового значення (початковий масив з нулів, масив сортується, надходять значення по одному, запам'ятовується індекс кожного нового значення і проводиться заміна найдавнішнього; тобто у відсортовому масиві замінюється лише одне значення, а не сортується кожен раз копія всього масиву).
pawa запропонував свій варіант медіанного фільтра:
Код: Виділити все
/* https://radioman.com.ua */
int MedianFilter(int[] window)
{
//Sortuvannya
for (int i = 0; i < window.Length - 1; i++)
{
for (int j = 0; j < window.Length - 1 - i; j++)
{
if (window[j] > window[j + 1])
{
int tmp = window[j];
window[j] = window[j + 1];
window[j + 1] = tmp;
}
}
}
//Povernennya seredn`ogo
int index = window.Length / 2;
if ((window.Length & 0x01) == 1){
return window[index];
}else{
return (window[index] + window[index - 1]) / 2;
}
}
Наступною перепоною було те, що у функцію не можна передати масив, а лише посилання на нього. Таким чином функція працює, і модифікує (!), з основним масивом. Для уникнення змін в основному масиві, прийшлось зробити його копіювання в допоміжний вже у самій функції.
В результаті отримано наступний коод медіанного фільтра:
Код: Виділити все
/* медіана pawa */
/* https://radioman.com.ua */
int window[] = {12, 2, 4, 8, 10, 6}; // тестовий масив
int newArray[sizeof (window) >> 2]; // копія тестового масиву
unsigned long time1; // значення лічильника "micros" перед початком виконання функції
unsigned long time2; // значення лічильника "micros" після виконання функції
unsigned long time0; // час виконання функції
int intWDI; // медіана
void setup() {
Serial.begin(115200);
}
void loop()
{
time1 = micros();
//intWDI = MedianFilter(window, sizeof (window) / sizeof(int)); // отримання медіани
//intWDI = MedianFilter(window, sizeof (window)/4); // те саме, sizeof(int)=4
intWDI = MedianFilter(window, sizeof (window) >> 2); // те саме, ділення 4 можна виконати зсувом вправо на 2
time2 = micros();
time0 = time2 - time1;
//Serial.print("WDI = "); Serial.println(intWDI); // вивід в монітор медіани
Serial.print("Time = "); Serial.print(time0); Serial.println(" mks"); // вивід в монітор часу виконання функції
}
/* функція розрахунку медіани*/
int MedianFilter(int *window, int intArrSize) // аргументами функції є посилання на масив та довжина масиву
{
// копіювання масиву
byte c;
for (c = 0; c < intArrSize; c++)
{
newArray[c] = window[c];
}
//"бульбашкове" сортування
for (int i = 0; i < intArrSize - 1; i++)
{
for (int j = 0; j < intArrSize - 1 - i; j++)
{
if (newArray[j] > newArray[j + 1])
{
int tmp = newArray[j];
newArray[j] = newArray[j + 1];
newArray[j + 1] = tmp;
}
}
}
//повернення середнього
int index = intArrSize / 2;
if ((intArrSize & 0x01) == 1) {
return newArray[index];
} else {
int WDI = (newArray[index] + newArray[index - 1]) / 2;
//int WDI = intArrSize;
//int WDI = window[index];
return WDI;
}
}