web-dev-qa-db-ja.com

iOS心拍数検出アルゴリズム

開発中のアプリにハートビート録音機能を実装しようとしています。

これを行うための好ましい方法は、ライトをオンにしてiPhoneのカメラを使用し、ユーザーにレンズに指を置いてもらい、ユーザーの心臓に対応するビデオフィードの変動を検出することです。

私は次のスタックオーバーフローの質問 here で非常に良い出発点を見つけました

この質問は、ハートビート時間のグラフをプロットするのに役立つコードを提供します。

これは、AVCaptureSessionを開始してカメラのライトをオンにする方法を示しています。

session = [[AVCaptureSession alloc] init];

AVCaptureDevice* camera = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if([camera isTorchModeSupported:AVCaptureTorchModeOn]) {
    [camera lockForConfiguration:nil];
    camera.torchMode=AVCaptureTorchModeOn;
    //  camera.exposureMode=AVCaptureExposureModeLocked;
    [camera unlockForConfiguration];
}
// Create a AVCaptureInput with the camera device
NSError *error=nil;
AVCaptureInput* cameraInput = [[AVCaptureDeviceInput alloc] initWithDevice:camera error:&error];
if (cameraInput == nil) {
    NSLog(@"Error to create camera capture:%@",error);
}

// Set the output
AVCaptureVideoDataOutput* videoOutput = [[AVCaptureVideoDataOutput alloc] init];

// create a queue to run the capture on
dispatch_queue_t captureQueue=dispatch_queue_create("catpureQueue", NULL);

// setup our delegate
[videoOutput setSampleBufferDelegate:self queue:captureQueue];

// configure the pixel format
videoOutput.videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], (id)kCVPixelBufferPixelFormatTypeKey,
                             nil];
videoOutput.minFrameDuration=CMTimeMake(1, 10);

// and the size of the frames we want
[session setSessionPreset:AVCaptureSessionPresetLow];

// Add the input and output
[session addInput:cameraInput];
[session addOutput:videoOutput];

// Start the session
[session startRunning];

この例のSelfは<AVCaptureVideoDataOutputSampleBufferDelegate>である必要があります。したがって、生のカメラデータを取得するには、次のメソッドを実装する必要があります。

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
static int count=0;
count++;
// only run if we're not already processing an image
// this is the image buffer
CVImageBufferRef cvimgRef = CMSampleBufferGetImageBuffer(sampleBuffer);
// Lock the image buffer
CVPixelBufferLockBaseAddress(cvimgRef,0);
// access the data
int width=CVPixelBufferGetWidth(cvimgRef);
int height=CVPixelBufferGetHeight(cvimgRef);
// get the raw image bytes
uint8_t *buf=(uint8_t *) CVPixelBufferGetBaseAddress(cvimgRef);
size_t bprow=CVPixelBufferGetBytesPerRow(cvimgRef);
float r=0,g=0,b=0;
for(int y=0; y<height; y++) {
    for(int x=0; x<width*4; x+=4) {
        b+=buf[x];
        g+=buf[x+1];
        r+=buf[x+2];
        //          a+=buf[x+3];
    }
    buf+=bprow;
}
r/=255*(float) (width*height);
g/=255*(float) (width*height);
b/=255*(float) (width*height);

float h,s,v;

RGBtoHSV(r, g, b, &h, &s, &v);

// simple highpass and lowpass filter 

static float lastH=0;
float highPassValue=h-lastH;
lastH=h;
float lastHighPassValue=0;
float lowPassValue=(lastHighPassValue+highPassValue)/2;

lastHighPassValue=highPassValue;

    //low pass value can now be used for basic heart beat detection


}

RGBはHSVに変換され、変動が監視されるのは色相です。

そしてRGB to HSVは次のように実装されています

void RGBtoHSV( float r, float g, float b, float *h, float *s, float *v ) {
float min, max, delta; 
min = MIN( r, MIN(g, b )); 
max = MAX( r, MAX(g, b )); 
*v = max;
delta = max - min; 
if( max != 0 )
    *s = delta / max;
else {
    // r = g = b = 0 
    *s = 0; 
    *h = -1; 
    return;
}
if( r == max )
    *h = ( g - b ) / delta; 
else if( g == max )
    *h=2+(b-r)/delta;
else 
    *h=4+(r-g)/delta; 
*h *= 60;
if( *h < 0 ) 
    *h += 360;
}

capureOutput:で計算されたローパス値は、最初は不規則なデータを提供しますが、次のように安定します。

2013-11-04 16:18:13.619 SampleHeartRateApp[1743:1803] -0.071218
2013-11-04 16:18:13.719 SampleHeartRateApp[1743:1803] -0.050072
2013-11-04 16:18:13.819 SampleHeartRateApp[1743:1803] -0.011375
2013-11-04 16:18:13.918 SampleHeartRateApp[1743:1803] 0.018456
2013-11-04 16:18:14.019 SampleHeartRateApp[1743:1803] 0.059024
2013-11-04 16:18:14.118 SampleHeartRateApp[1743:1803] 0.052198
2013-11-04 16:18:14.219 SampleHeartRateApp[1743:1803] 0.078189
2013-11-04 16:18:14.318 SampleHeartRateApp[1743:1803] 0.046035
2013-11-04 16:18:14.419 SampleHeartRateApp[1743:1803] -0.113153
2013-11-04 16:18:14.519 SampleHeartRateApp[1743:1803] -0.079792
2013-11-04 16:18:14.618 SampleHeartRateApp[1743:1803] -0.027654
2013-11-04 16:18:14.719 SampleHeartRateApp[1743:1803] -0.017288

最初に提供される不規則なデータの例は次のとおりです。

2013-11-04 16:17:28.747 SampleHeartRateApp[1743:3707] 17.271435
2013-11-04 16:17:28.822 SampleHeartRateApp[1743:1803] -0.049067
2013-11-04 16:17:28.922 SampleHeartRateApp[1743:1803] -6.524201
2013-11-04 16:17:29.022 SampleHeartRateApp[1743:1803] -0.766260
2013-11-04 16:17:29.137 SampleHeartRateApp[1743:3707] 9.956407
2013-11-04 16:17:29.221 SampleHeartRateApp[1743:1803] 0.076244
2013-11-04 16:17:29.321 SampleHeartRateApp[1743:1803] -1.049292
2013-11-04 16:17:29.422 SampleHeartRateApp[1743:1803] 0.088634
2013-11-04 16:17:29.522 SampleHeartRateApp[1743:1803] -1.035559
2013-11-04 16:17:29.621 SampleHeartRateApp[1743:1803] 0.019196
2013-11-04 16:17:29.719 SampleHeartRateApp[1743:1803] -1.027754
2013-11-04 16:17:29.821 SampleHeartRateApp[1743:1803] 0.045803
2013-11-04 16:17:29.922 SampleHeartRateApp[1743:1803] -0.857693
2013-11-04 16:17:30.021 SampleHeartRateApp[1743:1803] 0.061945
2013-11-04 16:17:30.143 SampleHeartRateApp[1743:1803] -0.701269

ハートビートがあるときはいつでも、ローパス値は正になります。だから私は基本的に現在の値を見て、それが正かどうかを確認する非常に単純なライブ検出アルゴリズムを試してみました、それは前の値も見て、負の場合は正になることを検出してビープ音を鳴らします。

これに関する問題は、データが上記のように常に完全であるとは限らないことです。時々、負の読み取り値の中に異常な正の読み取り値があり、逆もまた同様です。

時間内のローパス値のグラフは次のようになります。 enter image description here

興味深いことに、上記の異常は非常に一般的です。しばらくグラフを記録すると、非常によく似た形状の異常が複数回表示されます。

私の非常に単純なビート検出アルゴリズムでは、上記のような異常が発生した場合、検出期間(10秒)でカウントされたビートの数が4ビートまたは5ビート増加します。これにより、計算されたBPMは非常に不正確になります。しかし、それはそれがそうであるのと同じくらい簡単で、時間のおよそ70%は働きます。

この問題に対処するために、次のことを試しました。

1.配列の最後の3つのローパス値の記録を開始しました

2.次に、中間値の前後に2つの小さい値があるかどうかを確認しました。 (基本的なピーク検出)

3.このシナリオをビートとしてカウントし、それを所定の時間の現在のビートの合計に追加しました。

ただし、この方法は他の異常と同様に脆弱です。そして、実際にはより悪い方法であるように見えました。 (検出後にライブビープ音を鳴らすと、ポジティブからネガティブのアルゴリズムよりもはるかに不安定に見えました)

私の質問は、適度な精度で心拍が発生したことを確実に検出できるアルゴリズムを思い付くのに役立ちますか?

私が対処しなければならないもう1つの問題は、ユーザーの指がレンズ上にあるかどうかを検出することです。

不規則なローパス値を検出することを考えましたが、ローパスフィルターの問題は、不規則な値を考慮し、時間をかけて平滑化することです。ですので、助けていただければ幸いです。

御時間ありがとうございます。

40
Sam

信号を処理するためにいくつかのことを行う必要があり、これを行うための単一の「正しい」方法がないため、この質問への答えは少し複雑です。ただし、フィルターには band-pass filter を使用します。このタイプのフィルターでは、ハイエンドとローエンドの両方で受け入れられる周波数の範囲を指定できます。人間の心臓の鼓動については、これらの境界がどうあるべきか(40 bpm以上250 bpm以下)がわかっているため、この範囲外の周波数を除去するフィルターを作成できます。フィルターはまた、データがゼロを中心とするように移動するため、ピーク検出がはるかに容易になります。このフィルターは、ユーザーが指の圧力を(ある程度)上げたり下げたりした場合でも、より滑らかな信号を提供します。その後、追加の平滑化と外れ値の除去も行う必要があります。

私が使用した特定のタイプのバンドパスフィルターはバターワースフィルターです。フィルターは、データを収集する頻度に基づいて変化するため、手動で作成するのは少し複雑です。幸いなことに、これに役立つウェブサイトがあります here 。 30 fpsでデータを収集している場合、周波数は30 Hzになります。

これらすべてをまとめて、iOSアプリストアのアプリに含めるのに十分なほどユーザーの心拍数を検出するプロジェクトを作成しました。 github で心拍数検出コードを利用できるようにしました。

5
lehn0058

自分の指を使っていると思います。不規則な心拍がないと確信していますか?加えて、あなたはしたい不規則な心拍数]を持つ人々を処理します。言い換えれば、さまざまな入力値。彼らは心臓の問題を抱えている可能性が高いかもしれないので、あなたの両親または他の年上の親類に間違いなくそれを試してください。それ以外の場合、基本的な問題は、入力ソースのノイズが大きくなることです。あなたは基本的にそのノイズから信号を回復しようとしています。時にはそれが不可能になることがあり、レポートにノイズを焼き付けたいか、ノイズが多すぎる場合はデータストリームを無視するかを決定する必要があります。

続けてみてください異なるフィルター値;多分あなたはさらに低いパスフィルターが必要です。コメントから、あなたのローパスフィルターは良くなかったようです。 Webでのフィルタリングに関するリソースはたくさんあります。アルゴリズムをテストするための最良の方法となる優れた視覚化ツールがある場合。

データのダウンサンプリングを試すことができます。これにより、データがスムーズになります。 discard samples値を完全に破棄するか、前のサンプルと次のサンプルの平均で置き換えるか、所定の値にクランプするか、または計算された最大値。

これらの種類のアプリケーションで私の最大の牛肉はhiccupsが実際のライブデータとして扱われることです。私のジムのバイクの1つでは、bpmの測定値が役に立たないことがあります。これは、時々、自分の脈拍を見つけることができず、突然私の心が300 bpmに向かっていると思っているためです。 (そうでない;私は私の医者に尋ねた。)20分のセッションのためにそれが持っている平均は役に立たない。 「最後の20秒をこのアルゴリズムに詰め込もうとしたが、ここにゴミが出てきた」というよりは、(たとえば)過去10回の通常の拍動と異常率の平均を確認する方が便利だと思います。異常を示す別のフィルターを作成できれば、はるかに便利なアプリケーションになると思います。

1
AndrewS

私は...するだろう :

1)ピークからピークまでの期間を検出...期間が特定の期間のしきい値内で一貫している場合。HBを有効としてフラグを立てます。

2)外れ値検出アルゴリズムを実装します...特定の正規化されたしきい値を超えるビート。外れ値と見なされるため、最後に検出されたビートを代わりに使用してBPMを計算します。

もう1つのより複雑なアプローチは、カルマンフィルターまたはなんらかの種類を使用して、bpmを予測し、より正確な読み取りを取得することです。読み取りが無効であるとアプリが感知し、読書。

1
Kiko Lobo

すでに別の方法があるかもしれませんが、小さな メディアンフィルター を使用してみてください。たとえば、各出力値の3〜7個の入力値の中央値を使用すると、非非微小データの全体的な形状を破壊することなく、これらのピークを平滑化できます。

0
user1118321

単一のハートビートを「手動で」検出しようとしていますが、それほど堅牢ではありません。私はあなたの最善の策はピッチまたは周波数検出ライブラリのようなものだと思います(色の変化の周波数を検出するための計算と音の周波数を検出するための計算は同じでなければなりません)。

たぶん aubio のようなもの this で見つけたstackoverflowの検索「周波数検出」への答えが役立つかもしれません。それ以外の場合は、ウィキペディアで ピッチ検出 および/またはそこにある大量の信号処理ライブラリを確認してください。

0
HBu

まず、レンズの問題を解決します。指がレンズの上にあるときは、静的な黒いフレームは表示されません(ご想像のとおり)。アンビエントライトは実際に指を通過して赤みがかったフレームを作成します。また、指の血流パターンにより、その赤いフレームにわずかな周期的な変動が生じます(カメラアプリを開いて、指をレンズに完全に置いてみてください)。また、周辺光が十分でない場合は、いつでもカメラのフラッシュ/トーチをオンにしてそれを補正できます。

オープンソース(およびステップバイステップ)の手順については、以下を試してください。

http://www.ignaciomellado.es/blog/Measuring-heart-rate-with-a-smartphone-camera

また、パルス測定に関する次の特許を読むことをお勧めします。

http://www.google.com/patents/WO2013042070A1?cl=en

0
Ameer Sheikh

GPUImage フィルター、平均色、露出を使用してパルスを検出するプロジェクトを作成しました。パルスは、フィルター処理された画像の緑成分の移動平均に基づいて推定されます。

光パルスリーダー

0
Vijay