Commit c3e34f8a authored by Jean-Baptiste Mardelle's avatar Jean-Baptiste Mardelle
Browse files

Update motion tracker to display keyframes directly in the effect and make...

Update motion tracker to display keyframes directly in the effect and make them editable. Requires latest MLT git
parent a17e839c
......@@ -48,7 +48,7 @@
<name>Motion Tracker</name>
<description>Select a zone to follow its movements</description>
<author>Jean-Baptiste Mardelle</author>
<parameter type="geometry" name="rect" default="50% 50% 25% 25%" fixed="1" opacity="false" conditional="1">
<parameter type="animatedrect" name="results" default="50% 50% 25% 25%" opacity="false" conditional="1">
<name>Rectangle</name>
</parameter>
<parameter type="listdependency" name="algo" default="KCF" paramlist="KCF;CSRT;MOSSE;MIL;BOOSTING;MEDIANFLOW;DaSIAM" conditional="1">
......@@ -70,7 +70,7 @@
<parameter type="color" name="shape_color" default="0xff0000ff">
<name>Shape color</name>
</parameter>
<parameter type="constant" name="blur" max="200" min="0" default="0">
<parameter type="constant" name="blur" max="200" min="0" default="5">
<name>Blur</name>
</parameter>
<parameter type="list" name="blur_type" default="2" paramlist="0;1;2;3">
......@@ -79,17 +79,14 @@
</parameter>
<parameter type="hidden" name="modelsfolder" default="">
</parameter>
<parameter type="readonly" name="results" value="">
<name>Tracking data</name>
<comment>Click to copy to clipboard</comment>
</parameter>
<parameter type="filterjob" filtertag="opencv.tracker" consumer="null" consumerparams="all=1 terminate_on_pause=1 audio_off=1 no_meta=1 real_time=-1">
<name conditional="Reset">Analyse</name>
<jobparam name="conditionalinfo">Filter is in preview mode. Click Analyse to see real effect</jobparam>
<jobparam name="key">results</jobparam>
<jobparam name="keydefault">50% 50% 25% 25%</jobparam>
<jobparam name="finalfilter">opencv.tracker</jobparam>
<jobparam name="displaydataname">Motion tracking</jobparam>
<jobparam name="relativeInOut">1</jobparam>
<jobparam name="displaydataname">Motion tracking</jobparam>
</parameter>
</effect>
</group>
......@@ -945,7 +945,6 @@ QVariant KeyframeModel::getInterpolatedValue(const GenTime &pos) const
bool useOpacity = false;
if (auto ptr = m_model.lock()) {
ptr->passProperties(mlt_prop);
ptr->data(m_index, AssetParameterModel::ParentInRole).toInt();
out = ptr->data(m_index, AssetParameterModel::ParentDurationRole).toInt();
useOpacity = ptr->data(m_index, AssetParameterModel::OpacityRole).toBool();
animData = ptr->data(m_index, AssetParameterModel::ValueRole).toString();
......
......@@ -1205,7 +1205,8 @@ void AssetParameterModel::setParameters(const paramVector &params, bool update)
m_ownerId.first = ObjectType::NoItem;
}
for (const auto &param : params) {
setParameter(param.first, param.second.toString(), false);
QModelIndex ix = index(m_rows.indexOf(param.first), 0);
setParameter(param.first, param.second.toString(), false, ix);
}
if (m_keyframes) {
m_keyframes->refresh();
......
......@@ -40,6 +40,7 @@ ButtonParamWidget::ButtonParamWidget(std::shared_ptr<AssetParameterModel> model,
#endif
QString conditionalInfo;
QString defaultValue;
for (const QVariant &jobElement : qAsConst(filterData)) {
QStringList d = jobElement.toStringList();
if (d.size() == 2) {
......@@ -47,6 +48,8 @@ ButtonParamWidget::ButtonParamWidget(std::shared_ptr<AssetParameterModel> model,
conditionalInfo = d.at(1);
} else if (d.at(0) == QLatin1String("key")) {
m_keyParam = d.at(1);
} else if (d.at(0) == QLatin1String("keydefault")) {
defaultValue = d.at(1);
}
}
}
......@@ -54,7 +57,7 @@ ButtonParamWidget::ButtonParamWidget(std::shared_ptr<AssetParameterModel> model,
m_displayConditional = true;
for (const auto &param : qAsConst(filterParams)) {
if (param.first == m_keyParam) {
if (!param.second.toString().isEmpty()) {
if (!param.second.toString().isEmpty() && param.second.toString().contains(QLatin1Char(';'))) {
m_displayConditional = false;
}
break;
......@@ -78,11 +81,11 @@ ButtonParamWidget::ButtonParamWidget(std::shared_ptr<AssetParameterModel> model,
setMinimumHeight(m_button->sizeHint().height() + (m_label != nullptr ? m_label->sizeHint().height() : 0));
// emit the signal of the base class when appropriate
connect(this->m_button, &QPushButton::clicked, this, [&, filterData, filterAddedParams, consumerParams]() {
connect(this->m_button, &QPushButton::clicked, this, [&, filterData, filterAddedParams, consumerParams, defaultValue]() {
// Trigger job
if (!m_displayConditional) {
QVector<QPair<QString, QVariant>> values;
values << QPair<QString, QVariant>(m_keyParam,QVariant());
values << QPair<QString, QVariant>(m_keyParam,defaultValue);
auto *command = new AssetUpdateCommand(m_model, values);
pCore->pushUndo(command);
return;
......@@ -116,6 +119,16 @@ ButtonParamWidget::ButtonParamWidget(std::shared_ptr<AssetParameterModel> model,
for (const auto &param : qAsConst(filterLastParams)) {
if (param.first != m_keyParam) {
fParams.insert({param.first, param.second});
} else {
QString initialRect = param.second.toString();
if (initialRect.contains(QLatin1Char('='))) {
initialRect = initialRect.section(QLatin1Char('='), 1);
}
if (initialRect.contains(QLatin1Char(';'))) {
initialRect = initialRect.section(QLatin1Char(';'), 0, 0);
}
qDebug()<<"=== PASSING INITIAL RECT: "<<initialRect;
fParams.insert({QStringLiteral("rect"), initialRect});
}
}
for (const QString &fparam : filterAddedParams) {
......@@ -149,7 +162,7 @@ void ButtonParamWidget::slotRefresh()
QVector<QPair<QString, QVariant>> filterParams = m_model->getAllParameters();
m_displayConditional = true;
for (const auto &param : qAsConst(filterParams)) {
if (param.first == m_keyParam && !param.second.isNull()) {
if (param.first == m_keyParam && !param.second.isNull() && param.second.toString().contains(QLatin1Char(';'))) {
m_displayConditional = false;
break;
}
......
......@@ -1877,6 +1877,39 @@ bool DocumentValidator::upgrade(double version, const double currentVersion)
}
}
}
// Doc 1.1: Kdenlive 21.12.0
if (version < 1.1) {
// OpenCV tracker: Fix for older syntax where filter had in/out defined
QDomNodeList effects = m_doc.elementsByTagName(QStringLiteral("filter"));
int max = effects.count();
QStringList changedEffects;
for (int i = 0; i < max; ++i) {
QDomElement t = effects.at(i).toElement();
QString kdenliveId = Xml::getXmlProperty(t, QStringLiteral("kdenlive_id"));
if (kdenliveId == QLatin1String("opencv.tracker") && t.hasAttribute(QLatin1String("in"))) {
QString filterIn = t.attribute(QLatin1String("in"));
int inPoint;
Mlt::Properties props;
props.set("_profile", pCore->getProjectProfile()->get_profile(), 0);
if (!filterIn.contains(QLatin1Char(':'))) {
inPoint = filterIn.toInt();
} else {
// Convert from hh:mm:ss.mmm to frames
inPoint = props.time_to_frames(filterIn.toUtf8().constData());
}
qDebug()<<"=== FOUND TRACKER WITH IN POINT: "<<inPoint;
QString animation = Xml::getXmlProperty(t, QStringLiteral("results"));
props.set("key", animation.toUtf8().constData());
// This is a fake query to force the animation to be parsed
(void)props.anim_get_double("key", 0, -1);
Mlt::Animation anim = props.get_animation("key");
anim.shift_frames(inPoint);
Xml::setXmlProperty(t, QStringLiteral("results"), qstrdup(anim.serialize_cut()));
t.removeAttribute("in");
t.removeAttribute("out");
}
}
}
m_modified = true;
return true;
......
......@@ -56,8 +56,7 @@ void FilterTask::run()
QString url;
auto binClip = pCore->projectItemModel()->getClipByBinID(m_binId);
std::unique_ptr<Mlt::Producer> producer;
//std::unique_ptr<Mlt::Producer> wholeProducer;
std::unique_ptr<Mlt::Producer> producer = nullptr;
Mlt::Profile profile(pCore->getCurrentProfilePath().toUtf8().constData());
if (binClip) {
// Filter applied on a timeline or bin clip
......@@ -91,8 +90,6 @@ void FilterTask::run()
}
if (m_inPoint != 0 || m_outPoint != producer->get_length() - 1) {
producer->set_in_and_out(m_inPoint, m_outPoint);
//std::swap(wholeProducer, producer);
//producer.reset(wholeProducer->cut(m_inPoint, m_outPoint));
}
} else {
// Filter applied on a track of master producer, leave config to source job
......@@ -104,7 +101,7 @@ void FilterTask::run()
}
}
if ((producer == nullptr) || !producer->is_valid()) {
if (producer == nullptr || !producer->is_valid()) {
// Clip was removed or something went wrong, Notify user?
QMetaObject::invokeMethod(pCore.get(), "displayBinMessage", Qt::QueuedConnection, Q_ARG(QString, i18n("Cannot open source.")),
Q_ARG(int, int(KMessageWidget::Warning)));
......@@ -139,7 +136,7 @@ void FilterTask::run()
consumer->connect(*producer.get());
producer->set_speed(0);
producer->seek(0);
if (binClip) {
// Build filter
Mlt::Filter filter(profile, m_filterName.toUtf8().data());
......@@ -166,7 +163,7 @@ void FilterTask::run()
if (m_filterData.find(QLatin1String("relativeInOut")) != m_filterData.end()) {
// leave it operate on full clip
} else {
filter.set_in_and_out(producer->get_in(), producer->get_out());
filter.set_in_and_out(m_inPoint, m_outPoint);
}
producer->attach(filter);
filter.set("id", "kdenlive-analysis");
......@@ -246,12 +243,12 @@ void FilterTask::run()
}
}
params.append({key,QVariant(resultData)});
if (m_inPoint > 0 && (m_filterData.find(QLatin1String("relativeInOut")) != m_filterData.end())) {
// Motion tracker keyframes always start at master clip 0, so we need to set in/out points
if (m_inPoint > 0 && (m_filterData.find(QLatin1String("relativeInOut")) == m_filterData.end())) {
// Motion tracker keyframes always start at master clip 0, so no need to set in/out points
params.append({QStringLiteral("in"), m_inPoint});
params.append({QStringLiteral("out"), m_outPoint});
}
params.append({key,QVariant(resultData)});
if (m_filterData.find(QStringLiteral("storedata")) != m_filterData.end()) {
// Store a copy of the data in clip analysis
QString dataName = (m_filterData.find(QStringLiteral("displaydataname")) != m_filterData.end()) ? m_filterData.at(QStringLiteral("displaydataname")) : QStringLiteral("data");
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment