Магия с сенсором освещения
Помимо сенсора внезапного движения многие новые «Макбуки» оборудованы сенсором освещённости, он едва заметно располагается рядом с видеокамерой.
Мне интересно стало попробовать поработать и с ним тоже. На этот раз цель была — научиться эффектным жестом блокировать свой ноут. Конечно, ложных срабатываний масса — всё-таки этот сенсор крайне примитивен, он видит только освещённость, но в качестве фокуса годится:
По-моему, довольно эффектно смотрится — блокировка экрана магическим пассом.
Код на Си получился чуть более ста строк и на этот раз я распознаю шаблон поведения, он задаётся в подстановке «+vvv000-^^^», что означает «сначала идёт положительное отклонение от среднего, потом в последовательности не менее трёх, отклонение должно уменьшаться, потом не менее трёх раз должно встретиться нулевое отклонение от среднего, потом отрицательное отклонение, потом отклонение должно увеличиваться».
При сканировании чисел с датчика я всегда рассматриваю десять значений от которых считаю среднее и каждое значение рассматриваю как отклонение от этого среднего, собственно, вот код:
// Магические пассы, улавливаемые через датчик освещённости
// Written by Evgeny Stepanischev Jun 2013
// gcc lightlock.c -framework IOKit -framework ApplicationServices -O3 -o lightlock
#include <IOKit/IOKitLib.h>
#include <CoreFoundation/CoreFoundation.h>
#include <ApplicationServices/ApplicationServices.h>
#include <unistd.h>
#define WINDOWSIZE 10
#define GESTURES "+vvv000-^^^"
#define UPDATEINTERVAL .01
const kGetSensorReadingID = 0;
int makeReaction(uint64_t current);
static io_connect_t port = 0;
void macLock () {
CGSCreateLoginSession(NULL);
}
void updateTimerCallBack(CFRunLoopTimerRef timer, void *info) {
IOItemCount osize = 2;
uint64_t values[osize];
kern_return_t ret = IOConnectCallMethod(port, kGetSensorReadingID,
NULL, 0, NULL, 0, values, &osize, NULL, 0);
if (ret == KERN_SUCCESS) {
if (makeReaction(values[0])) {
macLock();
}
}
else if (ret != kIOReturnBusy) {
mach_error("IOConnectCallMethod: ", ret);
exit(-3);
}
}
int main() {
io_service_t service = IOServiceGetMatchingService(
kIOMasterPortDefault, IOServiceMatching("AppleLMUController")
);
if (service == kIOReturnNoDevice) {
fprintf(stderr, "Cannot find Ambient Light Sensor\n");
exit(-1);
}
kern_return_t ret = IOServiceOpen(service, mach_task_self(), 0, &port);
IOObjectRelease(service);
if (ret != KERN_SUCCESS) {
mach_error("IOServiceOpen:", ret);
exit(-2);
}
CFRunLoopTimerRef timer = CFRunLoopTimerCreate(
kCFAllocatorDefault,
CFAbsoluteTimeGetCurrent() + UPDATEINTERVAL,
UPDATEINTERVAL, 0, 0, updateTimerCallBack, NULL
);
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
CFRunLoopRun();
exit(0);
}
int checkMask(char mask, const int64_t diff, const int64_t prevdiff) {
switch (mask) {
case '+': return diff > 0;
case '-': return diff < 0;
case '0': return diff == 0;
case 'v': return prevdiff > diff;
case '^': return diff > prevdiff;
}
return 0;
}
int makeReaction(uint64_t current) {
static uint64_t window[WINDOWSIZE];
static uint64_t prevdiff = 0;
static gesturepos = 0;
uint64_t average = current;
int i, notzerocnt = 1;
for (i = WINDOWSIZE-1; i > 0; i--) {
if (window[i] = window[i-1]) {
average += window[i];
notzerocnt++;
}
}
window[0] = current;
average /= notzerocnt;
int64_t diff = average - current;
if (checkMask(GESTURES[gesturepos], diff, prevdiff)) {
gesturepos++;
} else {
if (!gesturepos || !checkMask(GESTURES[gesturepos-1], diff, prevdiff)) {
gesturepos = 0;
}
}
prevdiff = diff;
return gesturepos == sizeof GESTURES - 1;
}
В функции main, в конце стоит магический код, который нужен для того, чтобы вызываемые мною внутренние шестерёнки «Мака» благополучно крутились, иначе функция CGSCreateLoginSession будет ждать когда ей перепадёт процессорного времени.
Ну и традиционно, если кто-то что-то понимает в Си, поругайте код. Заранее спасибо!
Лучше бы автоматическую подстройку яркости сделал. Или она там и так есть?
Комментарий для Анонимус:
Она там есть.
http://img-fotki.yandex.ru/get/9150/35419492.c7/0_72239_6f444133_L#%D0%A1%D0%BD%D0%B8%D0%BC%D0%BE%D0%BA%2B%D1%8D%D0%BA%D1%80%D0%B0%D0%BD%D0%B0%2B2013-06-08%2B%D0%B2%2B13.23.57.png%7Chttp%3A%2F%2Ffotki.yandex.ru%2Fusers%2Fbolknote%2Fview%2F467513%2F%3Fpage%3D3#500x376
Используй вебкамеру и opencv. Тогда будет выше точность. Можно,кстати, совместить датчик и камеру.
Другое дело, что я не знаю насколько это оправданно и не накладно по ресурсам держать камеру постоянно включенной.
Еще читал не давно, что заморачиваются с уровнями сигналов стандартов 802.11 и судя по статье точность определения жестов достаточно высокая.
Комментарий для bagir:
Ирек, у меня нет нет цели решить какую-то утилитарную задачу, мне этого на работе хватает. Мне интересно сделать что-то необычное.
Насколько я помню ту статью, там можно только определить где стоят люди, это всё.
Определять где и сколько людей стоят уже давно можно. Думаю даже в домашних условиях можно в короткие сроки разработать нечто подобное.
А вот с жестами куда интереснее. Как работает — не знаю.
Комментарий для bagir:
Не факт, что мой ноут имеет всё необходимое, чтобы это анализировать.