Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dilate/Erode node #1845

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/node/factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include "effect/opacity/opacityeffect.h"
#include "filter/blur/blur.h"
#include "filter/dropshadow/dropshadowfilter.h"
#include "filter/dilateerode/dilateerode.h"
#include "filter/mosaic/mosaicfilternode.h"
#include "filter/stroke/stroke.h"
#include "generator/matrix/matrix.h"
Expand Down Expand Up @@ -309,6 +310,8 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id)
return new RippleDistortNode();
case kMulticamNode:
return new MultiCamNode();
case kDilateErodeFilter:
return new DilateErodeFilterNode();

case kInternalNodeCount:
break;
Expand Down
1 change: 1 addition & 0 deletions app/node/factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class NodeFactory
kTileDistort,
kSwirlDistort,
kMulticamNode,
kDilateErodeFilter,

// Count value
kInternalNodeCount
Expand Down
1 change: 1 addition & 0 deletions app/node/filter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ add_subdirectory(blur)
add_subdirectory(dropshadow)
add_subdirectory(mosaic)
add_subdirectory(stroke)
add_subdirectory(dilateerode)

set(OLIVE_SOURCES
${OLIVE_SOURCES}
Expand Down
22 changes: 22 additions & 0 deletions app/node/filter/dilateerode/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Olive - Non-Linear Video Editor
# Copyright (C) 2021 Olive Team
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

set(OLIVE_SOURCES
${OLIVE_SOURCES}
node/filter/dilateerode/dilateerode.h
node/filter/dilateerode/dilateerode.cpp
PARENT_SCOPE
)
100 changes: 100 additions & 0 deletions app/node/filter/dilateerode/dilateerode.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/***

Olive - Non-Linear Video Editor
Copyright (C) 2021 Olive Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

***/

#include "dilateerode.h"

namespace olive {

const QString DilateErodeFilterNode::kTextureInput = QStringLiteral("tex_in");
const QString DilateErodeFilterNode::kMethodInput = QStringLiteral("method_in");
const QString DilateErodeFilterNode::kPixelsInput = QStringLiteral("pixels_in");

#define super Node

DilateErodeFilterNode::DilateErodeFilterNode()
{
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));

AddInput(kMethodInput, NodeValue::kCombo, 0);

AddInput(kPixelsInput, NodeValue::kInt, 0);

SetFlags(kVideoEffect);
SetEffectInput(kTextureInput);
}

Node* DilateErodeFilterNode::copy() const
{
return new DilateErodeFilterNode();
}

QString DilateErodeFilterNode::Name() const
{
return tr("Dilate/Erode");
}

QString DilateErodeFilterNode::id() const
{
return QStringLiteral("org.olivevideoeditor.Olive.dilateerode");
}

QVector<Node::CategoryID> DilateErodeFilterNode::Category() const
{
return {kCategoryFilter};
}

QString DilateErodeFilterNode::Description() const
{
return tr("Grows or shrinks bright areas by the set number of pixels");
}

void DilateErodeFilterNode::Retranslate()
{
super::Retranslate();

SetInputName(kTextureInput, tr("Input"));
SetInputName(kMethodInput, tr("Method"));
SetComboBoxStrings(kMethodInput, {tr("Box"), tr("Distance"), tr("Gaussian")});
SetInputName(kPixelsInput, tr("Pixels"));
}

ShaderCode DilateErodeFilterNode::GetShaderCode(const ShaderRequest& request) const
{
Q_UNUSED(request)
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/dilateerode.frag"));
}

void DilateErodeFilterNode::Value(const NodeValueRow& value, const NodeGlobals& globals, NodeValueTable* table) const
{
// If there's no texture and no dilation/erosion, no need to run an operation
if (value[kTextureInput].toTexture() && value[kPixelsInput].data().toInt() != 0) {
TexturePtr tex = value[kTextureInput].toTexture();
//job.SetIterations(2, kTextureInput);
ShaderJob job(value);
job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, tex->virtual_resolution(), this));
job.SetIterations(2, kTextureInput);
table->Push(NodeValue::kTexture, tex->toJob(job), this);
} else {
// If we're not doing anything just push the texture
table->Push(value[kTextureInput]);
}
}

}
56 changes: 56 additions & 0 deletions app/node/filter/dilateerode/dilateerode.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/***

Olive - Non-Linear Video Editor
Copyright (C) 2021 Olive Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

***/

#ifndef DILATEERODEFILTERNODE_H
#define DILATEERODEFILTERNODE_H

#include "node/node.h"

namespace olive {

class DilateErodeFilterNode : public Node
{
Q_OBJECT
public:
DilateErodeFilterNode();

NODE_DEFAULT_DESTRUCTOR(DilateErodeFilterNode)

virtual Node* copy() const override;

virtual QString Name() const override;
virtual QString id() const override;
virtual QVector<CategoryID> Category() const override;
virtual QString Description() const override;

virtual void Retranslate() override;

virtual ShaderCode GetShaderCode(const ShaderRequest& request) const override;
virtual void Value(const NodeValueRow& value, const NodeGlobals& globals, NodeValueTable* table) const override;

static const QString kTextureInput;
static const QString kMethodInput;
static const QString kPixelsInput;

};

}

#endif //DILATEERODEFILTERNODE_H
81 changes: 81 additions & 0 deletions app/shaders/dilateerode.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
uniform sampler2D tex_in;
uniform int method_in;
uniform int pixels_in;
uniform vec2 resolution_in;

uniform int ove_iteration;

in vec2 ove_texcoord;
out vec4 frag_color;

// Gaussian function uses PI
#define M_PI 3.1415926535897932384626433832795

float gaussian2(float x, float y, float sigma) {
return (1.0/((sigma*sigma)*2.0*M_PI))*exp(-0.5*(((x*x) + (y*y))/(sigma*sigma)));
}

void main(void) {
vec2 pixel_coord;
vec2 offset;
vec4 sample;
vec4 composite;

int size = int(abs(float(pixels_in)));

pixel_coord = ove_texcoord;

// Constants for gaussian method
float sigma = float(size) / 2.0;
float max_weight = gaussian2(0.0, 0.0, sigma);
size*=3;

// GLSL has no FLOAT_MIN or FLOAT_MAX
// Gaussian erode essentially inverts the image, does a gaussian dilate and then
// reinverts the image hence there being a special case here for method_in == 2
composite = pixels_in > 0 || method_in == 2 ? vec4(-9999.0) : vec4(9999.0);
for(int i = -size; i <= size; i++) {

if(ove_iteration == 0){
offset.x = float(i) / resolution_in.x;
offset.y = 0.0;
} else{
offset.x = 0.0;
offset.y = float(i) / resolution_in.y;
}

if (method_in == 0) { // Box
sample = texture(tex_in, pixel_coord+offset);
if (pixels_in > 0) {
composite = max(sample, composite);
} else if (pixels_in < 0) {
composite = min(sample, composite);
}
} else if (method_in == 1) { // Distance
float len = length(offset);
float scaled_size = float(size) / length(resolution_in);
if (len <= scaled_size){
sample = texture(tex_in, pixel_coord+offset);
if (pixels_in > 0) {
composite = max(sample, composite);
} else if (pixels_in < 0) {
composite = min(sample, composite);
}
}
} else if (method_in == 2) { // Gaussian
float weight = gaussian2(float(i), 0.0, sigma) / max_weight;
sample = texture(tex_in, pixel_coord+offset);
if (pixels_in > 0) {
// weight^2 seems to give a better result
composite = max(sample*weight*weight, composite);
} else if (pixels_in < 0) {
composite = max((1.0-sample)*weight*weight, composite);
}
}
}
if (method_in == 2 && pixels_in < 0) {
frag_color = vec4(1.0-composite);
} else{
frag_color = vec4(composite);
}
}