1# Copyright (C) 2025 yuygfgg
3# This file is part of Vapoursynth-llvmexpr.
5# Vapoursynth-llvmexpr is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
10# Vapoursynth-llvmexpr is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
15# You should have received a copy of the GNU General Public License
16# along with Vapoursynth-llvmexpr. If not, see <https://www.gnu.org/licenses/>.
21@error This expr should be used in SingleExpr mode
25@error This expr requires exactly one input clip
28@if __INPUT_SAMPLETYPE_0__ == stFloat
31@define FG_VAL_INPUT 2 ** (__INPUT_BITDEPTH_0__ - 1)
34@if __OUTPUT_SAMPLETYPE__ == stFloat
35@define FG_VAL_OUTPUT 1
37@define FG_VAL_OUTPUT 2 ** (__OUTPUT_BITDEPTH__ - 1)
41@define MIN_SIZE_THR 100
43@define NUM_PIXELS __WIDTH__ * __HEIGHT__
45@define MAX_LABELS (NUM_PIXELS) / 2 + 2
47label_map = new(NUM_PIXELS);
48parent = new(MAX_LABELS);
49area = new(MAX_LABELS);
54dx[1] = -1; dy[1] = -1;
60while (i < MAX_LABELS) {
66while (i < NUM_PIXELS) {
72function find_root(Value i) {
74 while (parent[root] != root) {
77 while (parent[i] != root) {
86function union_sets(Value i, Value j) {
87 i_root = find_root(i);
88 j_root = find_root(j);
89 if (i_root != j_root) {
90 if (i_root < j_root) {
91 parent[j_root] = i_root;
93 parent[i_root] = j_root;
98# Labeling and Recording Equivalences
101while (y < __HEIGHT__) {
103 while (x < __WIDTH__) {
104 pixel_val = dyn($x, x, y, 0);
106 if (pixel_val != BG_VAL) {
107 neighbor_labels = new(4);
115 # Boundary check (ny is always <= y, so only need ny >= 0)
116 if (nx >= 0 && nx < __WIDTH__ && ny >= 0) {
117 if (dyn($x, nx, ny, 0) != BG_VAL) {
118 neighbor_labels[n_count] = label_map[ny * __WIDTH__ + nx];
119 n_count = n_count + 1;
126 label_map[y * __WIDTH__ + x] = next_label;
127 next_label = next_label + 1;
129 current_label = neighbor_labels[0];
132 current_label = min(current_label, neighbor_labels[k]);
136 label_map[y * __WIDTH__ + x] = current_label;
140 union_sets(current_label, neighbor_labels[k]);
150# Resolve Labels and Calculate Area
152while (y < __HEIGHT__) {
154 while (x < __WIDTH__) {
155 idx = y * __WIDTH__ + x;
156 if (label_map[idx] > 0) {
157 root = find_root(label_map[idx]);
158 label_map[idx] = root;
159 area[root] = area[root] + 1;
166# Generate Final Output
168while (y < __HEIGHT__) {
170 while (x < __WIDTH__) {
172 label = label_map[y * __WIDTH__ + x];
174 if (area[label] >= MIN_SIZE_THR) {
175 final_val = FG_VAL_INPUT;
178 store(x, y, 0, final_val);