void TrackerNodePrivate::computeCornerParamsFromTracksEnd(double refTime, double maxFittingError, const QList<CornerPinData>& results) { // Make sure we get only valid results QList<CornerPinData> validResults; for (QList<CornerPinData>::const_iterator it = results.begin(); it != results.end(); ++it) { if (it->valid) { validResults.push_back(*it); } } // Get all knobs that we are going to write to and block any value changes on them KnobIntPtr smoothCornerPinKnob = smoothCornerPin.lock(); int smoothJitter = smoothCornerPinKnob->getValue(); int halfJitter = smoothJitter / 2; KnobDoublePtr fittingErrorKnob = fittingError.lock(); KnobDoublePtr fromPointsKnob[4]; KnobDoublePtr toPointsKnob[4]; KnobBoolPtr enabledPointsKnob[4]; KnobStringPtr fittingWarningKnob = fittingErrorWarning.lock(); for (int i = 0; i < 4; ++i) { fromPointsKnob[i] = fromPoints[i].lock(); toPointsKnob[i] = toPoints[i].lock(); enabledPointsKnob[i] = enableToPoint[i].lock(); } std::list<KnobIPtr> animatedKnobsChanged; fittingErrorKnob->blockValueChanges(); animatedKnobsChanged.push_back(fittingErrorKnob); for (int i = 0; i < 4; ++i) { toPointsKnob[i]->blockValueChanges(); animatedKnobsChanged.push_back(toPointsKnob[i]); } // Get reference corner pin CornerPinPoints refFrom; for (int c = 0; c < 4; ++c) { refFrom.pts[c].x = fromPointsKnob[c]->getValueAtTime(refTime); refFrom.pts[c].y = fromPointsKnob[c]->getValueAtTime(refTime, DimIdx(1)); } // Create temporary curves and clone the toPoint internal curves at once because setValueAtTime will be slow since it emits // signals to create keyframes in keyframeSet Curve tmpToPointsCurveX[4], tmpToPointsCurveY[4]; Curve tmpFittingErrorCurve; bool mustShowFittingWarn = false; for (QList<CornerPinData>::const_iterator itResults = validResults.begin(); itResults != validResults.end(); ++itResults) { const CornerPinData& dataAtTime = *itResults; // Add the error to the curve and check if we need to turn on the RMS warning { KeyFrame kf(dataAtTime.time, dataAtTime.rms); if (dataAtTime.rms >= maxFittingError) { mustShowFittingWarn = true; } tmpFittingErrorCurve.addKeyFrame(kf); } if (smoothJitter <= 1) { for (int c = 0; c < 4; ++c) { Point toPoint; toPoint = TrackerHelper::applyHomography(refFrom.pts[c], dataAtTime.h); KeyFrame kx(dataAtTime.time, toPoint.x); KeyFrame ky(dataAtTime.time, toPoint.y); tmpToPointsCurveX[c].addKeyFrame(kx); tmpToPointsCurveY[c].addKeyFrame(ky); //toPoints[c]->setValuesAtTime(dataAtTime[i].time, toPoint.x, toPoint.y, ViewSpec::all(), eValueChangedReasonNatronInternalEdited); } } else { // Average to points before and after if using jitter CornerPinPoints avgTos; averageDataFunctor<QList<CornerPinData>::const_iterator, CornerPinPoints, CornerPinPoints>(validResults.begin(), validResults.end(), itResults, halfJitter, &refFrom, &avgTos, 0); for (int c = 0; c < 4; ++c) { KeyFrame kx(dataAtTime.time, avgTos.pts[c].x); KeyFrame ky(dataAtTime.time, avgTos.pts[c].y); tmpToPointsCurveX[c].addKeyFrame(kx); tmpToPointsCurveY[c].addKeyFrame(ky); } } // use jitter } // for each result // If user wants a post-smooth, apply it if (smoothJitter > 1) { int halfSmoothJitter = smoothJitter / 2; KeyFrameSet xSet[4], ySet[4]; KeyFrameSet newXSet[4], newYSet[4]; for (int c = 0; c < 4; ++c) { xSet[c] = tmpToPointsCurveX[c].getKeyFrames_mt_safe(); ySet[c] = tmpToPointsCurveY[c].getKeyFrames_mt_safe(); } for (int c = 0; c < 4; ++c) { for (KeyFrameSet::const_iterator it = xSet[c].begin(); it != xSet[c].end(); ++it) { double avg; averageDataFunctor<KeyFrameSet::const_iterator, void, double>(xSet[c].begin(), xSet[c].end(), it, halfSmoothJitter, 0, &avg, 0); KeyFrame k(*it); k.setValue(avg); newXSet[c].insert(k); } for (KeyFrameSet::const_iterator it = ySet[c].begin(); it != ySet[c].end(); ++it) { double avg; averageDataFunctor<KeyFrameSet::const_iterator, void, double>(ySet[c].begin(), ySet[c].end(), it, halfSmoothJitter, 0, &avg, 0); KeyFrame k(*it); k.setValue(avg); newYSet[c].insert(k); } } for (int c = 0; c < 4; ++c) { tmpToPointsCurveX[c].setKeyframes(newXSet[c], true); tmpToPointsCurveY[c].setKeyframes(newYSet[c], true); } } fittingWarningKnob->setSecret(!mustShowFittingWarn); fittingErrorKnob->cloneCurve(ViewIdx(0), DimIdx(0), tmpFittingErrorCurve, 0 /*offset*/, 0 /*range*/, 0 /*stringAnim*/); for (int c = 0; c < 4; ++c) { toPointsKnob[c]->cloneCurve(ViewIdx(0), DimIdx(0), tmpToPointsCurveX[c], 0 /*offset*/, 0 /*range*/, 0 /*stringAnim*/); toPointsKnob[c]->cloneCurve(ViewIdx(0), DimIdx(1), tmpToPointsCurveY[c], 0 /*offset*/, 0 /*range*/, 0 /*stringAnim*/); } for (std::list<KnobIPtr>::iterator it = animatedKnobsChanged.begin(); it != animatedKnobsChanged.end(); ++it) { (*it)->unblockValueChanges(); (*it)->evaluateValueChange(DimSpec::all(), refTime, ViewSetSpec::all(), eValueChangedReasonNatronInternalEdited); } endSolve(); } // TrackerNodePrivate::computeCornerParamsFromTracksEnd