Mickey::Mickey() : updateTimer(this), btnThread(this), state(STANDBY), calDlg(), aplDlg(), recenterFlag(true), dw(NULL) { trans = new MickeyTransform(); //QObject::connect(onOffSwitch, SIGNAL(activated()), this, SLOT(onOffSwitch_activated())); //QObject::connect(&lbtnSwitch, SIGNAL(activated()), &btnThread, SLOT(on_key_pressed())); QObject::connect(this, SIGNAL(mouseHotKey_activated(int, bool)), &btnThread, SLOT(on_mouseHotKey_activated(int, bool))); QObject::connect(&updateTimer, SIGNAL(timeout()), this, SLOT(updateTimer_activated())); QObject::connect(&aplDlg, SIGNAL(keep()), this, SLOT(keepSettings())); QObject::connect(&aplDlg, SIGNAL(revert()), this, SLOT(revertSettings())); QObject::connect(&calDlg, SIGNAL(recenterNow(bool)), this, SLOT(recenterNow(bool))); QObject::connect(&calDlg, SIGNAL(startCalibration()), this, SLOT(startCalibration())); QObject::connect(&calDlg, SIGNAL(finishCalibration()), this, SLOT(finishCalibration())); QObject::connect(&calDlg, SIGNAL(cancelCalibration(bool)), this, SLOT(cancelCalibration(bool))); if(!mouse.init()){ exit(1); } dw = QApplication::desktop(); // screenBBox = dw->screenGeometry(); screenBBox = QRect(0, 0, dw->width(), dw->height()); screenCenter = screenBBox.center(); updateTimer.setSingleShot(false); updateTimer.setInterval(8); btnThread.start(); linuxtrack_init((char *)"Mickey"); changeState(TRACKING); }
void MickeyCalibration::timeout() { //std::cout<<"timer!"<<std::endl; --cntr; if(cntr == 0){ switch(calState){ case CENTER_ONLY: timer.stop(); emit recenterNow(true); window()->hide(); break; case CENTER: emit recenterNow(false); emit startCalibration(); calState = CALIBRATE; cntr = GUI.getCalDelay(); break; case CALIBRATE: timer.stop(); emit finishCalibration(); window()->hide(); break; } } switch(calState){ case CENTER: case CENTER_ONLY: ui.CalibrationText->setText(QString::fromUtf8("Please center your head and sight.")); ui.FBString->setText(QString::fromUtf8("Center position will be recorded in %1 seconds...").arg(cntr)); break; case CALIBRATE: ui.CalibrationText->setText(QString::fromUtf8("Please move your head to left/right and up/down extremes.")); ui.FBString->setText(QString::fromUtf8("Calibration will end in %1 seconds...").arg(cntr)); break; } }
void DdeFaceTracker::decodePacket(const QByteArray& buffer) { _lastReceiveTimestamp = usecTimestampNow(); if (buffer.size() > MIN_PACKET_SIZE) { bool isFiltering = Menu::getInstance()->isOptionChecked(MenuOption::VelocityFilter); Packet packet; int bytesToCopy = glm::min((int)sizeof(packet), buffer.size()); memset(&packet.name, '\n', MAX_NAME_SIZE + 1); memcpy(&packet, buffer.data(), bytesToCopy); glm::vec3 translation; memcpy(&translation, packet.translation, sizeof(packet.translation)); glm::quat rotation; memcpy(&rotation, &packet.rotation, sizeof(packet.rotation)); if (_reset || (_lastMessageReceived == 0)) { memcpy(&_referenceTranslation, &translation, sizeof(glm::vec3)); memcpy(&_referenceRotation, &rotation, sizeof(glm::quat)); _reset = false; } // Compute relative translation float LEAN_DAMPING_FACTOR = 75.0f; translation -= _referenceTranslation; translation /= LEAN_DAMPING_FACTOR; translation.x *= -1; if (isFiltering) { glm::vec3 linearVelocity = (translation - _lastHeadTranslation) / _averageMessageTime; const float LINEAR_VELOCITY_FILTER_STRENGTH = 0.3f; float velocityFilter = glm::clamp(1.0f - glm::length(linearVelocity) * LINEAR_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f); _filteredHeadTranslation = velocityFilter * _filteredHeadTranslation + (1.0f - velocityFilter) * translation; _lastHeadTranslation = translation; _headTranslation = _filteredHeadTranslation; } else { _headTranslation = translation; } // Compute relative rotation rotation = glm::inverse(_referenceRotation) * rotation; if (isFiltering) { glm::quat r = rotation * glm::inverse(_headRotation); float theta = 2 * acos(r.w); glm::vec3 angularVelocity; if (theta > EPSILON) { float rMag = glm::length(glm::vec3(r.x, r.y, r.z)); angularVelocity = theta / _averageMessageTime * glm::vec3(r.x, r.y, r.z) / rMag; } else { angularVelocity = glm::vec3(0, 0, 0); } const float ANGULAR_VELOCITY_FILTER_STRENGTH = 0.3f; _headRotation = safeMix(_headRotation, rotation, glm::clamp(glm::length(angularVelocity) * ANGULAR_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f)); } else { _headRotation = rotation; } // Translate DDE coefficients to Faceshift compatible coefficients for (int i = 0; i < NUM_EXPRESSIONS; i++) { _coefficients[DDE_TO_FACESHIFT_MAPPING[i]] = packet.expressions[i]; } // Calibration if (_isCalibrating) { addCalibrationDatum(); } for (int i = 0; i < NUM_FACESHIFT_BLENDSHAPES; i++) { _coefficients[i] -= _coefficientAverages[i]; } // Use BrowsU_C to control both brows' up and down float browUp = _coefficients[_browUpCenterIndex]; if (isFiltering) { const float BROW_VELOCITY_FILTER_STRENGTH = 0.5f; float velocity = fabs(browUp - _lastBrowUp) / _averageMessageTime; float velocityFilter = glm::clamp(velocity * BROW_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f); _filteredBrowUp = velocityFilter * browUp + (1.0f - velocityFilter) * _filteredBrowUp; _lastBrowUp = browUp; browUp = _filteredBrowUp; _coefficients[_browUpCenterIndex] = browUp; } _coefficients[_browUpLeftIndex] = browUp; _coefficients[_browUpRightIndex] = browUp; _coefficients[_browDownLeftIndex] = -browUp; _coefficients[_browDownRightIndex] = -browUp; // Offset jaw open coefficient static const float JAW_OPEN_THRESHOLD = 0.1f; _coefficients[_jawOpenIndex] = _coefficients[_jawOpenIndex] - JAW_OPEN_THRESHOLD; // Offset smile coefficients static const float SMILE_THRESHOLD = 0.5f; _coefficients[_mouthSmileLeftIndex] = _coefficients[_mouthSmileLeftIndex] - SMILE_THRESHOLD; _coefficients[_mouthSmileRightIndex] = _coefficients[_mouthSmileRightIndex] - SMILE_THRESHOLD; // Velocity filter EyeBlink values const float DDE_EYEBLINK_SCALE = 3.0f; float eyeBlinks[] = { DDE_EYEBLINK_SCALE * _coefficients[_leftBlinkIndex], DDE_EYEBLINK_SCALE * _coefficients[_rightBlinkIndex] }; if (isFiltering) { const float BLINK_VELOCITY_FILTER_STRENGTH = 0.3f; for (int i = 0; i < 2; i++) { float velocity = fabs(eyeBlinks[i] - _lastEyeBlinks[i]) / _averageMessageTime; float velocityFilter = glm::clamp(velocity * BLINK_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f); _filteredEyeBlinks[i] = velocityFilter * eyeBlinks[i] + (1.0f - velocityFilter) * _filteredEyeBlinks[i]; _lastEyeBlinks[i] = eyeBlinks[i]; } } // Finesse EyeBlink values float eyeCoefficients[2]; if (Menu::getInstance()->isOptionChecked(MenuOption::BinaryEyelidControl)) { if (_eyeStates[0] == EYE_UNCONTROLLED) { _eyeStates[0] = EYE_OPEN; _eyeStates[1] = EYE_OPEN; } for (int i = 0; i < 2; i++) { // Scale EyeBlink values so that they can be used to control both EyeBlink and EyeOpen // -ve values control EyeOpen; +ve values control EyeBlink static const float EYE_CONTROL_THRESHOLD = 0.5f; // Resting eye value eyeCoefficients[i] = (_filteredEyeBlinks[i] - EYE_CONTROL_THRESHOLD) / (1.0f - EYE_CONTROL_THRESHOLD); // Change to closing or opening states const float EYE_CONTROL_HYSTERISIS = 0.25f; float eyeClosingThreshold = getEyeClosingThreshold(); float eyeOpeningThreshold = eyeClosingThreshold - EYE_CONTROL_HYSTERISIS; if ((_eyeStates[i] == EYE_OPEN || _eyeStates[i] == EYE_OPENING) && eyeCoefficients[i] > eyeClosingThreshold) { _eyeStates[i] = EYE_CLOSING; } else if ((_eyeStates[i] == EYE_CLOSED || _eyeStates[i] == EYE_CLOSING) && eyeCoefficients[i] < eyeOpeningThreshold) { _eyeStates[i] = EYE_OPENING; } const float EYELID_MOVEMENT_RATE = 10.0f; // units/second const float EYE_OPEN_SCALE = 0.2f; if (_eyeStates[i] == EYE_CLOSING) { // Close eyelid until it's fully closed float closingValue = _lastEyeCoefficients[i] + EYELID_MOVEMENT_RATE * _averageMessageTime; if (closingValue >= 1.0) { _eyeStates[i] = EYE_CLOSED; eyeCoefficients[i] = 1.0; } else { eyeCoefficients[i] = closingValue; } } else if (_eyeStates[i] == EYE_OPENING) { // Open eyelid until it meets the current adjusted value float openingValue = _lastEyeCoefficients[i] - EYELID_MOVEMENT_RATE * _averageMessageTime; if (openingValue < eyeCoefficients[i] * EYE_OPEN_SCALE) { _eyeStates[i] = EYE_OPEN; eyeCoefficients[i] = eyeCoefficients[i] * EYE_OPEN_SCALE; } else { eyeCoefficients[i] = openingValue; } } else if (_eyeStates[i] == EYE_OPEN) { // Reduce eyelid movement eyeCoefficients[i] = eyeCoefficients[i] * EYE_OPEN_SCALE; } else if (_eyeStates[i] == EYE_CLOSED) { // Keep eyelid fully closed eyeCoefficients[i] = 1.0; } } if (_eyeStates[0] == EYE_OPEN && _eyeStates[1] == EYE_OPEN) { // Couple eyelids eyeCoefficients[0] = eyeCoefficients[1] = (eyeCoefficients[0] + eyeCoefficients[0]) / 2.0f; } _lastEyeCoefficients[0] = eyeCoefficients[0]; _lastEyeCoefficients[1] = eyeCoefficients[1]; } else { _eyeStates[0] = EYE_UNCONTROLLED; _eyeStates[1] = EYE_UNCONTROLLED; eyeCoefficients[0] = _filteredEyeBlinks[0]; eyeCoefficients[1] = _filteredEyeBlinks[1]; } // Use EyeBlink values to control both EyeBlink and EyeOpen if (eyeCoefficients[0] > 0) { _coefficients[_leftBlinkIndex] = eyeCoefficients[0]; _coefficients[_leftEyeOpenIndex] = 0.0f; } else { _coefficients[_leftBlinkIndex] = 0.0f; _coefficients[_leftEyeOpenIndex] = -eyeCoefficients[0]; } if (eyeCoefficients[1] > 0) { _coefficients[_rightBlinkIndex] = eyeCoefficients[1]; _coefficients[_rightEyeOpenIndex] = 0.0f; } else { _coefficients[_rightBlinkIndex] = 0.0f; _coefficients[_rightEyeOpenIndex] = -eyeCoefficients[1]; } // Scale all coefficients for (int i = 0; i < NUM_EXPRESSIONS; i++) { _blendshapeCoefficients[i] = glm::clamp(DDE_COEFFICIENT_SCALES[i] * _coefficients[i], 0.0f, 1.0f); } // Calculate average frame time const float FRAME_AVERAGING_FACTOR = 0.99f; quint64 usecsNow = usecTimestampNow(); if (_lastMessageReceived != 0) { _averageMessageTime = FRAME_AVERAGING_FACTOR * _averageMessageTime + (1.0f - FRAME_AVERAGING_FACTOR) * (float)(usecsNow - _lastMessageReceived) / 1000000.0f; } _lastMessageReceived = usecsNow; FaceTracker::countFrame(); } else { qCWarning(interfaceapp) << "DDE Face Tracker: Decode error"; } if (_isCalibrating && _calibrationCount > CALIBRATION_SAMPLES) { finishCalibration(); } }