|
| 1 | +obs = obslua |
| 2 | +bit = require("bit") |
| 3 | + |
| 4 | +MAX_KERNEL_SIZE = 16 -- max.: 3 * MAX_SIGMA |
| 5 | +MAX_SIGMA = 10.0 |
| 6 | + |
| 7 | +SETTING_KERNEL_SIZE = 'kernel_size' |
| 8 | +SETTING_SIGMA = 'sigma' |
| 9 | +SETTING_USE_MASK = 'use_mask' |
| 10 | +SETTING_INVERT_MASK = 'invert_mask' |
| 11 | +SETTING_MASK_IMAGE = 'mask_image' |
| 12 | + |
| 13 | +TEXT_KERNEL_SIZE = 'Kernel Size' |
| 14 | +TEXT_SIGMA = 'Sigma' |
| 15 | +TEXT_USE_MASK = 'Use Blur Mask' |
| 16 | +TEXT_INVERT_MASK = 'Invert Blur Mask' |
| 17 | +TEXT_MASK = 'Blur Mask Image (Alpha)' |
| 18 | + |
| 19 | +IMAGE_FILTER = 'Images (*.bmp *.jpg *.jpeg *.tga *.gif *.png);; All Files (*.*)' |
| 20 | + |
| 21 | +source_def = {} |
| 22 | +source_def.id = 'filter_gaussian_blur' |
| 23 | +source_def.type = obs.OBS_SOURCE_TYPE_FILTER |
| 24 | +source_def.output_flags = bit.bor(obs.OBS_SOURCE_VIDEO) |
| 25 | + |
| 26 | +function gaussian(sigma, x) |
| 27 | + factor = 1.0 / math.sqrt(2 * math.pi * sigma * sigma) |
| 28 | + exponent = -1 * (x * x) / (2 * sigma * sigma); |
| 29 | + return factor * math.exp(exponent) |
| 30 | +end |
| 31 | + |
| 32 | +function set_render_size(filter) |
| 33 | + target = obs.obs_filter_get_target(filter.context) |
| 34 | + |
| 35 | + local width, height |
| 36 | + if target == nil then |
| 37 | + width = 0 |
| 38 | + height = 0 |
| 39 | + else |
| 40 | + width = obs.obs_source_get_base_width(target) |
| 41 | + height = obs.obs_source_get_base_height(target) |
| 42 | + end |
| 43 | + |
| 44 | + filter.render_width = width |
| 45 | + filter.render_height = height |
| 46 | + filter.pixel_size.x = 1.0 / width |
| 47 | + filter.pixel_size.y = 1.0 / height |
| 48 | +end |
| 49 | + |
| 50 | +source_def.get_name = function() |
| 51 | + return 'Gaussian Blur' |
| 52 | +end |
| 53 | + |
| 54 | +source_def.destroy = function(filter) |
| 55 | + if filter.effect ~= nil then |
| 56 | + obs.obs_enter_graphics() |
| 57 | + obs.gs_effect_destroy(filter.effect) |
| 58 | + obs.obs_leave_graphics() |
| 59 | + end |
| 60 | +end |
| 61 | + |
| 62 | +source_def.update = function(filter, settings) |
| 63 | + local kernel_size = obs.obs_data_get_int(settings, SETTING_KERNEL_SIZE) |
| 64 | + filter.kernel_size = math.ceil(kernel_size / 2) |
| 65 | + filter.sigma = obs.obs_data_get_double(settings, SETTING_SIGMA) |
| 66 | + filter.use_mask = obs.obs_data_get_bool(settings, SETTING_USE_MASK) |
| 67 | + filter.invert_mask = obs.obs_data_get_bool(settings, SETTING_INVERT_MASK) |
| 68 | + local mask_image_path = obs.obs_data_get_string(settings, SETTING_MASK_IMAGE) |
| 69 | + |
| 70 | + local kernel = {} |
| 71 | + local sum = 0.0 |
| 72 | + for i = 1, filter.kernel_size do |
| 73 | + kernel[i] = gaussian(filter.sigma, i - 1) |
| 74 | + sum = sum + kernel[i] + kernel[i] |
| 75 | + end |
| 76 | + for i = filter.kernel_size + 1, MAX_KERNEL_SIZE do |
| 77 | + kernel[i] = 0.0 |
| 78 | + end |
| 79 | + sum = sum - kernel[1] |
| 80 | + local norm = 1.0 / sum |
| 81 | + for i = 1, MAX_KERNEL_SIZE do |
| 82 | + kernel[i] = kernel[i] * norm |
| 83 | + print(kernel[i]) |
| 84 | + end |
| 85 | + |
| 86 | + obs.vec4_set(filter.kernel0, kernel[1], kernel[2], kernel[3], kernel[4]) |
| 87 | + obs.vec4_set(filter.kernel1, kernel[5], kernel[6], kernel[7], kernel[8]) |
| 88 | + obs.vec4_set(filter.kernel2, kernel[9], kernel[10], kernel[11], kernel[12]) |
| 89 | + obs.vec4_set(filter.kernel3, kernel[13], kernel[14], kernel[15], kernel[16]) |
| 90 | + |
| 91 | + if filter.use_mask then |
| 92 | + obs.obs_enter_graphics() |
| 93 | + obs.gs_image_file_free(filter.mask_image) |
| 94 | + obs.obs_leave_graphics() |
| 95 | + |
| 96 | + obs.gs_image_file_init(filter.mask_image, mask_image_path) |
| 97 | + |
| 98 | + obs.obs_enter_graphics() |
| 99 | + obs.gs_image_file_init_texture(filter.mask_image) |
| 100 | + obs.obs_leave_graphics() |
| 101 | + |
| 102 | + if not filter.mask_image.loaded then |
| 103 | + print("failed to load texture " .. mask_image_path); |
| 104 | + end |
| 105 | + end |
| 106 | +end |
| 107 | + |
| 108 | +source_def.create = function(settings, source) |
| 109 | + filter = {} |
| 110 | + effect_path = script_path() .. 'filter-gaussian-blur/filter-gaussian-blur.effect' |
| 111 | + |
| 112 | + filter.context = source |
| 113 | + |
| 114 | + filter.mask_image = obs.gs_image_file() |
| 115 | + |
| 116 | + filter.pixel_size = obs.vec2() |
| 117 | + |
| 118 | + filter.kernel_size = 4 |
| 119 | + filter.kernel0 = obs.vec4() |
| 120 | + filter.kernel1 = obs.vec4() |
| 121 | + filter.kernel2 = obs.vec4() |
| 122 | + filter.kernel3 = obs.vec4() |
| 123 | + |
| 124 | + set_render_size(filter) |
| 125 | + |
| 126 | + obs.obs_enter_graphics() |
| 127 | + |
| 128 | + filter.effect = obs.gs_effect_create_from_file(effect_path, nil) |
| 129 | + if filter.effect ~= nil then |
| 130 | + filter.mask_param = obs.gs_effect_get_param_by_name(filter.effect, 'mask') |
| 131 | + filter.use_mask_param = obs.gs_effect_get_param_by_name(filter.effect, 'use_mask') |
| 132 | + filter.invert_mask_param = obs.gs_effect_get_param_by_name(filter.effect, 'invert_mask') |
| 133 | + |
| 134 | + filter.pixel_size_param = obs.gs_effect_get_param_by_name(filter.effect, 'pixel_size') |
| 135 | + |
| 136 | + filter.kernel0_param = obs.gs_effect_get_param_by_name(filter.effect, 'kernel0') |
| 137 | + filter.kernel1_param = obs.gs_effect_get_param_by_name(filter.effect, 'kernel1') |
| 138 | + filter.kernel2_param = obs.gs_effect_get_param_by_name(filter.effect, 'kernel2') |
| 139 | + filter.kernel3_param = obs.gs_effect_get_param_by_name(filter.effect, 'kernel3') |
| 140 | + filter.kernel_size_param = obs.gs_effect_get_param_by_name(filter.effect, 'kernel_size') |
| 141 | + end |
| 142 | + |
| 143 | + obs.obs_leave_graphics() |
| 144 | + |
| 145 | + if filter.effect == nil then |
| 146 | + source_def.destroy(filter) |
| 147 | + return nil |
| 148 | + end |
| 149 | + |
| 150 | + source_def.update(filter, settings) |
| 151 | + return filter |
| 152 | +end |
| 153 | + |
| 154 | +source_def.get_width = function(filter) |
| 155 | + return filter.render_width |
| 156 | +end |
| 157 | + |
| 158 | +source_def.get_height = function(filter) |
| 159 | + return filter.render_height |
| 160 | +end |
| 161 | + |
| 162 | +source_def.video_render = function(filter, effect) |
| 163 | + obs.obs_source_process_filter_begin(filter.context, obs.GS_RGBA, obs.OBS_NO_DIRECT_RENDERING) |
| 164 | + |
| 165 | + obs.gs_effect_set_texture(filter.mask_param, filter.mask_image.texture) |
| 166 | + obs.gs_effect_set_bool(filter.use_mask_param, filter.use_mask) |
| 167 | + obs.gs_effect_set_bool(filter.invert_mask_param, filter.invert_mask) |
| 168 | + |
| 169 | + obs.gs_effect_set_vec2(filter.pixel_size_param, filter.pixel_size) |
| 170 | + |
| 171 | + obs.gs_effect_set_vec4(filter.kernel0_param, filter.kernel0) |
| 172 | + obs.gs_effect_set_vec4(filter.kernel1_param, filter.kernel1) |
| 173 | + obs.gs_effect_set_vec4(filter.kernel2_param, filter.kernel2) |
| 174 | + obs.gs_effect_set_vec4(filter.kernel3_param, filter.kernel3) |
| 175 | + obs.gs_effect_set_int(filter.kernel_size_param, filter.kernel_size) |
| 176 | + |
| 177 | + obs.obs_source_process_filter_end(filter.context, filter.effect, filter.render_width, filter.render_height) |
| 178 | +end |
| 179 | + |
| 180 | +source_def.get_properties = function(settings) |
| 181 | + props = obs.obs_properties_create() |
| 182 | + |
| 183 | + obs.obs_properties_add_int_slider(props, SETTING_KERNEL_SIZE, TEXT_KERNEL_SIZE, 1, MAX_KERNEL_SIZE * 2 - 1, 2) |
| 184 | + obs.obs_properties_add_float_slider(props, SETTING_SIGMA, TEXT_SIGMA, 1.0, MAX_SIGMA, 0.01) |
| 185 | + obs.obs_properties_add_bool(props, SETTING_USE_MASK, TEXT_USE_MASK) |
| 186 | + obs.obs_properties_add_bool(props, SETTING_INVERT_MASK, TEXT_INVERT_MASK) |
| 187 | + obs.obs_properties_add_path(props, SETTING_MASK_IMAGE, TEXT_MASK_IMAGE, obs.OBS_PATH_FILE, IMAGE_FILTER, nil) |
| 188 | + |
| 189 | + return props |
| 190 | +end |
| 191 | + |
| 192 | +source_def.get_defaults = function(settings) |
| 193 | + obs.obs_data_set_default_int(settings, SETTING_KERNEL_SIZE, 3) |
| 194 | + obs.obs_data_set_default_double(settings, SETTING_SIGMA, 1.0) |
| 195 | + obs.obs_data_set_default_bool(settings, SETTING_USE_MASK, false) |
| 196 | + obs.obs_data_set_default_bool(settings, SETTING_INVERT_MASK, false) |
| 197 | +end |
| 198 | + |
| 199 | +source_def.video_tick = function(filter, seconds) |
| 200 | + set_render_size(filter) |
| 201 | +end |
| 202 | + |
| 203 | +obs.obs_register_source(source_def) |
0 commit comments