Skip to content

Commit 81cefe6

Browse files
committed
[orx-fx] Add fade to Contour and CannyEdgeDetector
1 parent a080903 commit 81cefe6

File tree

6 files changed

+134
-45
lines changed

6 files changed

+134
-45
lines changed

orx-fx/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ kotlin {
3434
implementation(project(":orx-color"))
3535
implementation(project(":orx-fx"))
3636
implementation(project(":orx-noise"))
37+
implementation(project(":orx-shapes"))
38+
implementation(project(":orx-image-fit"))
3739
}
3840
}
3941
}

orx-fx/src/commonMain/kotlin/edges/CannyEdgeDetector.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ class CannyEdgeDetector : Filter1to1(
3737
var backgroundOpacity: Double by parameters
3838

3939

40+
@DoubleParameter("fade", 0.0, 1.0, order = 7)
41+
var fade: Double by parameters
42+
4043
init {
4144
threshold0 = 2.0
4245
threshold1 = 0.0
@@ -45,6 +48,7 @@ class CannyEdgeDetector : Filter1to1(
4548
backgroundColor = ColorRGBa.BLACK
4649
backgroundOpacity = 1.0
4750
foregroundOpacity = 1.0
51+
fade = 1.0
4852
}
4953

5054
}

orx-fx/src/commonMain/kotlin/edges/Contour.kt

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@ import org.openrndr.color.ColorRGBa
66
import org.openrndr.draw.Filter1to1
77
import org.openrndr.extra.fx.fx_contour
88
import org.openrndr.extra.fx.mppFilterShader
9-
import org.openrndr.extra.parameters.ColorParameter
10-
import org.openrndr.extra.parameters.Description
11-
import org.openrndr.extra.parameters.DoubleParameter
12-
import org.openrndr.extra.parameters.IntParameter
9+
import org.openrndr.extra.parameters.*
1310

1411
@Description("Contour")
1512
class Contour : Filter1to1(mppFilterShader(fx_contour, "contour")) {
@@ -28,13 +25,18 @@ class Contour : Filter1to1(mppFilterShader(fx_contour, "contour")) {
2825
@DoubleParameter("bias", -1.0, 1.0)
2926
var bias: Double by parameters
3027

31-
3228
@ColorParameter("contour color")
3329
var contourColor: ColorRGBa by parameters
3430

3531
@IntParameter("window", 0, 10)
3632
var window: Int by parameters
3733

34+
@BooleanParameter("output bands", order = 100)
35+
var outputBands: Boolean by parameters
36+
37+
@DoubleParameter("fade", 0.0, 1.0, order = 200)
38+
var fade: Double by parameters
39+
3840
init {
3941
levels = 6.0
4042
contourWidth = 0.4
@@ -43,5 +45,7 @@ class Contour : Filter1to1(mppFilterShader(fx_contour, "contour")) {
4345
contourOpacity = 1.0
4446
window = 1
4547
bias = 0.0
48+
outputBands = false
49+
fade = 1.0
4650
}
4751
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* Demonstrate the Contour filter
3+
* @author Edwin Jakobs
4+
*/
5+
6+
import org.openrndr.application
7+
import org.openrndr.color.ColorRGBa
8+
import org.openrndr.draw.createEquivalent
9+
import org.openrndr.draw.loadImage
10+
import org.openrndr.extra.fx.edges.Contour
11+
import org.openrndr.extra.imageFit.imageFit
12+
import org.openrndr.extra.shapes.primitives.grid
13+
14+
fun main() = application {
15+
configure {
16+
width = 720
17+
height = 720
18+
}
19+
program {
20+
val image = loadImage("demo-data/images/image-001.png")
21+
val contour = Contour()
22+
contour.levels = 4.0
23+
contour.window = 1
24+
contour.outputBands = true
25+
contour.contourColor = ColorRGBa.PINK
26+
contour.backgroundOpacity = 0.0
27+
28+
val edges = image.createEquivalent()
29+
extend {
30+
val cells = drawer.bounds.grid(2, 2).flatten()
31+
val actions = listOf(
32+
{
33+
contour.outputBands = true
34+
contour.levels = 2.0
35+
},
36+
{
37+
contour.outputBands = false
38+
contour.levels = 2.0
39+
},
40+
{
41+
contour.outputBands = false
42+
contour.levels = 8.0
43+
},
44+
{
45+
contour.outputBands = true
46+
contour.levels = 8.0
47+
},
48+
)
49+
for ((cell, action) in cells zip actions) {
50+
action()
51+
contour.apply(image, edges)
52+
drawer.imageFit(edges, cell)
53+
}
54+
}
55+
}
56+
}

orx-fx/src/shaders/glsl/edges/canny-edge-detector.frag

Lines changed: 41 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -15,44 +15,46 @@ uniform vec4 foregroundColor;
1515
uniform float backgroundOpacity;
1616
uniform float foregroundOpacity;
1717

18+
uniform float fade;
19+
1820
vec2 iResolution;
1921

20-
float getAve(vec2 uv){
22+
float getAve(vec2 uv) {
2123
vec3 rgb = texture(tex0, uv).rgb;
2224
vec3 lum = vec3(0.299, 0.587, 0.114);
2325
return dot(lum, rgb);
2426
}
2527

2628
// Detect edge.
27-
vec4 sobel(vec2 fragCoord, vec2 dir){
28-
vec2 uv = fragCoord/iResolution.xy;
29-
vec2 texel = 1./iResolution.xy;
30-
float np = getAve(uv + (vec2(-1,+1) + dir ) * texel * thickness);
31-
float zp = getAve(uv + (vec2( 0,+1) + dir ) * texel * thickness);
32-
float pp = getAve(uv + (vec2(+1,+1) + dir ) * texel * thickness);
33-
34-
float nz = getAve(uv + (vec2(-1, 0) + dir ) * texel * thickness);
29+
vec4 sobel(vec2 fragCoord, vec2 dir) {
30+
vec2 uv = fragCoord / iResolution.xy;
31+
vec2 texel = 1. / iResolution.xy;
32+
float np = getAve(uv + (vec2(-1, + 1) + dir) * texel * thickness);
33+
float zp = getAve(uv + (vec2(0, + 1) + dir) * texel * thickness);
34+
float pp = getAve(uv + (vec2(+ 1, + 1) + dir) * texel * thickness);
35+
36+
float nz = getAve(uv + (vec2(-1, 0) + dir) * texel * thickness);
3537
// zz = 0
36-
float pz = getAve(uv + (vec2(+1, 0) + dir ) * texel * thickness);
38+
float pz = getAve(uv + (vec2(+ 1, 0) + dir) * texel * thickness);
3739

38-
float nn = getAve(uv + (vec2(-1,-1) + dir ) * texel * thickness);
39-
float zn = getAve(uv + (vec2( 0,-1) + dir ) * texel * thickness);
40-
float pn = getAve(uv + (vec2(+1,-1) + dir ) * texel * thickness);
40+
float nn = getAve(uv + (vec2(-1, -1) + dir) * texel * thickness);
41+
float zn = getAve(uv + (vec2(0, -1) + dir) * texel * thickness);
42+
float pn = getAve(uv + (vec2(+ 1, -1) + dir) * texel * thickness);
4143

4244
// np zp pp
4345
// nz zz pz
4446
// nn zn pn
4547

4648
#if 0
47-
float gx = (np*-1. + nz*-2. + nn*-1. + pp*1. + pz*2. + pn*1.);
48-
float gy = (np*-1. + zp*-2. + pp*-1. + nn*1. + zn*2. + pn*1.);
49+
float gx = (np * -1. + nz * -2. + nn * -1. + pp * 1. + pz * 2. + pn * 1.);
50+
float gy = (np * -1. + zp * -2. + pp * -1. + nn * 1. + zn * 2. + pn * 1.);
4951
#else
5052
// https://www.shadertoy.com/view/Wds3Rl
51-
float gx = (np*-3. + nz*-10. + nn*-3. + pp*3. + pz*10. + pn*3.);
52-
float gy = (np*-3. + zp*-10. + pp*-3. + nn*3. + zn*10. + pn*3.);
53+
float gx = (np * -3. + nz * -10. + nn * -3. + pp * 3. + pz * 10. + pn * 3.);
54+
float gy = (np * -3. + zp * -10. + pp * -3. + nn * 3. + zn * 10. + pn * 3.);
5355
#endif
5456

55-
vec2 G = vec2(gx,gy);
57+
vec2 G = vec2(gx, gy);
5658

5759
float grad = length(G);
5860

@@ -62,49 +64,53 @@ vec4 sobel(vec2 fragCoord, vec2 dir){
6264
}
6365

6466
// Make edge thinner.
65-
vec2 hysteresisThr(vec2 fragCoord, float mn, float mx){
67+
vec2 hysteresisThr(vec2 fragCoord, float mn, float mx) {
6668

6769
vec4 edge = sobel(fragCoord, vec2(0));
6870

6971
vec2 dir = vec2(cos(edge.w), sin(edge.w));
70-
dir *= vec2(-1,1); // rotate 90 degrees.
72+
dir *= vec2(-1, 1); // rotate 90 degrees.
7173

7274
vec4 edgep = sobel(fragCoord, dir);
7375
vec4 edgen = sobel(fragCoord, -dir);
7476

75-
if(edge.z < edgep.z || edge.z < edgen.z ) edge.z = 0.;
77+
if (edge.z < edgep.z || edge.z < edgen.z) edge.z = 0.;
7678

7779
return vec2(
78-
(edge.z > mn) ? edge.z : 0.,
79-
(edge.z > mx) ? edge.z : 0.
80+
(edge.z > mn) ? edge.z : 0.,
81+
(edge.z > mx) ? edge.z : 0.
8082
);
8183
}
8284

83-
float cannyEdge(vec2 fragCoord, float mn, float mx){
85+
float cannyEdge(vec2 fragCoord, float mn, float mx) {
8486

85-
vec2 np = hysteresisThr(fragCoord + vec2(-1,+1), mn, mx);
86-
vec2 zp = hysteresisThr(fragCoord + vec2( 0,+1), mn, mx);
87-
vec2 pp = hysteresisThr(fragCoord + vec2(+1,+1), mn, mx);
87+
vec2 np = hysteresisThr(fragCoord + vec2(-1, + 1), mn, mx);
88+
vec2 zp = hysteresisThr(fragCoord + vec2(0, + 1), mn, mx);
89+
vec2 pp = hysteresisThr(fragCoord + vec2(+ 1, + 1), mn, mx);
8890

8991
vec2 nz = hysteresisThr(fragCoord + vec2(-1, 0), mn, mx);
90-
vec2 zz = hysteresisThr(fragCoord + vec2( 0, 0), mn, mx);
91-
vec2 pz = hysteresisThr(fragCoord + vec2(+1, 0), mn, mx);
92+
vec2 zz = hysteresisThr(fragCoord + vec2(0, 0), mn, mx);
93+
vec2 pz = hysteresisThr(fragCoord + vec2(+ 1, 0), mn, mx);
9294

93-
vec2 nn = hysteresisThr(fragCoord + vec2(-1,-1), mn, mx);
94-
vec2 zn = hysteresisThr(fragCoord + vec2( 0,-1), mn, mx);
95-
vec2 pn = hysteresisThr(fragCoord + vec2(+1,-1), mn, mx);
95+
vec2 nn = hysteresisThr(fragCoord + vec2(-1, -1), mn, mx);
96+
vec2 zn = hysteresisThr(fragCoord + vec2(0, -1), mn, mx);
97+
vec2 pn = hysteresisThr(fragCoord + vec2(+ 1, -1), mn, mx);
9698

9799
// np zp pp
98100
// nz zz pz
99101
// nn zn pn
100102
//return min(1., step(1e-3, zz.x) * (zp.y + nz.y + pz.y + zn.y)*8.);
101103
//return min(1., step(1e-3, zz.x) * (np.y + pp.y + nn.y + pn.y)*8.);
102-
return min(1., step(1e-2, zz.x*8.) * smoothstep(.0, .3, np.y + zp.y + pp.y + nz.y + pz.y + nn.y + zn.y + pn.y)*8.);
104+
return min(1., step(1e-2, zz.x * 8.) * smoothstep(.0, .3, np.y + zp.y + pp.y + nz.y + pz.y + nn.y + zn.y + pn.y) * 8.);
103105
}
104106

105-
void main(){
107+
void main() {
106108
iResolution = vec2(textureSize(tex0, 0));
109+
vec4 original = texture(tex0, v_texCoord0);
107110
vec2 fragCoord = v_texCoord0 * iResolution;
108111
float edge = cannyEdge(fragCoord, threshold0, threshold1);
109-
o_output = mix(foregroundColor * foregroundOpacity, backgroundColor * backgroundOpacity, 1.-edge);
112+
o_output = mix(original,
113+
mix(foregroundColor * foregroundOpacity,
114+
backgroundColor * backgroundOpacity, 1. - edge),
115+
fade);
110116
}

orx-fx/src/shaders/glsl/edges/contour.frag

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,44 @@ uniform vec4 contourColor;
88
uniform float backgroundOpacity;
99
uniform int window;
1010
uniform float bias;
11+
uniform bool outputBands;
12+
uniform float fade;
1113

12-
float calc_contour(vec2 uv) {
14+
vec2 calc_contour(vec2 uv) {
1315
vec4 box = texture(tex0, uv);
1416
float v = sin(3.1415926535 * levels * (dot(vec3(1.0 / 3.0), box.xyz) + bias));
15-
float level = floor((dot(vec3(1.0 / 3.0), box.xyz) + bias) * levels) / levels;
17+
float level = floor((dot(vec3(1.0 / 3.0), box.xyz) + bias) * levels);
1618
float contour = 1.0 - smoothstep(0., contourWidth, 0.5 * abs(v) / fwidth(v));
17-
return contour;
19+
return vec2(contour, level);
1820
}
1921

2022
void main() {
2123
vec2 step = 1.0 / vec2(textureSize(tex0, 0));
2224
float contour = 0.0;
2325
float weight = 0.0;
26+
float level = 0.0;
2427

2528
for (int i = -window; i <= window; ++i) {
2629
for (int j = -window; j <= window; ++j) {
27-
contour += calc_contour(v_texCoord0 + step / (float(window) + 1.0) * vec2(float(i), float(j)));
30+
vec2 c = calc_contour(v_texCoord0 + step / (float(window) + 1.0) * vec2(float(i), float(j)));
31+
contour += c.x;
32+
level += c.y;
2833
weight += 1.0;
2934
}
3035
}
3136
contour /= weight;
37+
3238
vec4 t = texture(tex0, v_texCoord0);
33-
o_output = t * backgroundOpacity * (1.0 - contour) + contour * contourColor * contourOpacity * t.a;
39+
40+
if (outputBands) {
41+
level /= weight;
42+
43+
level = 1.0 - max(0.0, fract(level / 2.0) * 2.0);
44+
contour = level;
45+
}
46+
47+
o_output = mix(t,
48+
t * backgroundOpacity * (1.0 - contour) + contour * contourColor * contourOpacity * clamp(t.a, 0.0, 1.0),
49+
fade);
50+
3451
}

0 commit comments

Comments
 (0)