Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save domadev812/1e86b731dfa8dffc840e72b107c92cb1 to your computer and use it in GitHub Desktop.
Save domadev812/1e86b731dfa8dffc840e72b107c92cb1 to your computer and use it in GitHub Desktop.
PubNub Heart Rate App for iOS
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
// if we're paused don't do anything
if(self.currentState==STATE_PAUSED) {
// reset our frame counter
self.validFrameCounter=0;
return;
}
// this is the image buffer
CVImageBufferRef cvimgRef = CMSampleBufferGetImageBuffer(sampleBuffer);
// Lock the image buffer
CVPixelBufferLockBaseAddress(cvimgRef,0);
// access the data
size_t width=CVPixelBufferGetWidth(cvimgRef);
size_t height=CVPixelBufferGetHeight(cvimgRef);
// get the raw image bytes
uint8_t *buf=(uint8_t *) CVPixelBufferGetBaseAddress(cvimgRef);
size_t bprow=CVPixelBufferGetBytesPerRow(cvimgRef);
// and pull out the average rgb value of the frame
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];
}
buf+=bprow;
}
r/=255*(float) (width*height);
g/=255*(float) (width*height);
b/=255*(float) (width*height);
// convert from rgb to hsv colourspace
float h,s,v;
RGBtoHSV(r, g, b, &h, &s, &v);
// do a sanity check to see if a finger is placed over the camera
if(s>0.5 && v>0.5) {
NSLog(@"RatePulse: %@",self.pulseRate.text);
// increment the valid frame count
self.validFrameCounter++;
// filter the hue value - the filter is a simple band pass filter that removes any DC component and any high frequency noise
float filtered=[self.filter processValue:h];
// have we collected enough frames for the filter to settle?
if(self.validFrameCounter > MIN_FRAMES_FOR_FILTER_TO_SETTLE) {
// add the new value to the pulse detector
[self.pulseDetector addNewValue:filtered atTime:CACurrentMediaTime()];
}
TimerBool=YES;
} else {
TimerBool=NO;
self.validFrameCounter = 0;
// clear the pulse detector - we only really need to do this once, just before we start adding valid samples
[self.pulseDetector reset];
}
}
- (IBAction)SaveDoctId_btn:(id)sender {
if ([_doctorId_txtfld.text length] == 0 ) {
UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"PubNub" message:@"Please enter DoctorId" delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
[alert show];
}
else{
[self.view endEditing:YES];
_backButton.hidden=NO;
self.pulseRate.text=@"PLACE FINGER ON CAMERA LENS";
[_heartImage setImage:[UIImage imageNamed:@"Black1_heart.png"]];
[UIView animateWithDuration:1.0
animations:^{
_doctorId_view.alpha = 0;
}
completion:^(BOOL finished){
[UIView animateWithDuration:1.0
animations:^{
_doctorId_view.alpha = 0;
}
completion:^(BOOL finished){
[_doctorId_view setHidden:YES];
}];
}];
}
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
PNConfiguration *configuration = [PNConfiguration configurationWithPublishKey:@"demo" subscribeKey:@"demo"];
self.client = [PubNub clientWithConfiguration:configuration];
[self.client addListener:self];
[self.client subscribeToChannels: @[@"doctor_id"] withPresence:YES];
return YES;
}
-(void)PublishOnPubNub :(NSString*)PulseRate docId:(NSString*)Docid{
NSString *DocId=[NSString stringWithFormat:@"%@heartbeat_alert",Docid];
[self.client publish: @"Hello from PubNub iOS!" toChannel: DocId storeInHistory:YES
withCompletion:^(PNPublishStatus *status) {
[self stopLoader];
// Check whether request successfully completed or not.
if (!status.isError) {
// Message successfully published to specified channel.
UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"PubNub" message:@"Your Message Successfuly sent to doctor" delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
[alert show];
}
// Request processing failed.
else {
UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"PubNub" message:@"Error Occurred !!" delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
[alert show];
// Handle message publish error. Check 'category' property to find out possible issue
// because of which request did fail.
//
// Request can be resent using: [status retry];
}
}];
}
-(float) addNewValue:(float) newVal atTime:(double) time {
// we keep track of the number of values above and below zero
if(newVal>0) {
upVals[upValIndex]=newVal;
upValIndex++;
if(upValIndex>=AVERAGE_SIZE) {
upValIndex=0;
}
}
if(newVal<0) {
downVals[downValIndex]=-newVal;
downValIndex++;
if(downValIndex>=AVERAGE_SIZE) {
downValIndex=0;
}
}
// work out the average value above zero
float count=0;
float total=0;
for(int i=0; i<AVERAGE_SIZE; i++) {
if(upVals[i]!=INVALID_ENTRY) {
count++;
total+=upVals[i];
}
}
float averageUp=total/count;
// and the average value below zero
count=0;
total=0;
for(int i=0; i<AVERAGE_SIZE; i++) {
if(downVals[i]!=INVALID_ENTRY) {
count++;
total+=downVals[i];
}
}
float averageDown=total/count;
// is the new value a down value?
if(newVal<-0.5*averageDown) {
wasDown=true;
}
// is the new value an up value and were we previously in the down state?
if(newVal>=0.5*averageUp && wasDown) {
wasDown=false;
// work out the difference between now and the last time this happenned
if(time-periodStart<MAX_PERIOD && time-periodStart>MIN_PERIOD) {
periods[periodIndex]=time-periodStart;
periodTimes[periodIndex]=time;
periodIndex++;
if(periodIndex>=MAX_PERIODS_TO_STORE) {
periodIndex=0;
}
}
// track when the transition happened
periodStart=time;
}
// return up or down
if(newVal<-0.5*averageDown) {
return -1;
} else if(newVal>0.5*averageUp) {
return 1;
}
return 0;
}
-(float) getAverage {
double time=CACurrentMediaTime();
double total=0;
double count=0;
for(int i=0; i<MAX_PERIODS_TO_STORE; i++) {
// only use upto 10 seconds worth of data
if(periods[i]!=INVALID_ENTRY && time-periodTimes[i]<10) {
count++;
total+=periods[i];
}
}
// do we have enough values?
if(count>2) {
return total/count;
}
return INVALID_PULSE_PERIOD;
}
-(void) startCameraCapture {
// [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(BlinkingMethod) userInfo:nil repeats:YES];
timer = [NSTimer scheduledTimerWithTimeInterval: 0.5
target: self
selector:@selector(BlinkingMethod)
userInfo: nil repeats:YES];
// Create the AVCapture Session
self.session = [[AVCaptureSession alloc] init];
// Get the default camera device
self.camera = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
// switch on torch mode - can't detect the pulse without it
if([self.camera isTorchModeSupported:AVCaptureTorchModeOn]) {
[self.camera lockForConfiguration:nil];
self.camera.torchMode=AVCaptureTorchModeOn;
[self.camera unlockForConfiguration];
}
// Create a AVCaptureInput with the camera device
NSError *error=nil;
AVCaptureInput* cameraInput = [[AVCaptureDeviceInput alloc] initWithDevice:self.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("captureQueue", NULL);
// setup ourself up as the capture delegate
[videoOutput setSampleBufferDelegate:self queue:captureQueue];
// configure the pixel format
videoOutput.videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], (id)kCVPixelBufferPixelFormatTypeKey, nil];
// set the minimum acceptable frame rate to 10 fps
videoOutput.minFrameDuration=CMTimeMake(1, 10);
// and the size of the frames we want - we'll use the smallest frame size available
[self.session setSessionPreset:AVCaptureSessionPresetLow];
// Add the input and output
[self.session addInput:cameraInput];
[self.session addOutput:videoOutput];
// Start the session
[self.session startRunning];
// we're now sampling from the camera
self.currentState=STATE_SAMPLING;
// stop the app from sleeping
[UIApplication sharedApplication].idleTimerDisabled = YES;
// update our UI on a timer every 0.1 seconds
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(update) userInfo:nil repeats:YES];
}
-(void) stopCameraCapture {
[self.session stopRunning];
self.session=nil;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment