gpt4 book ai didi

python - 在kivy中实现最优高斯模糊效果

转载 作者:行者123 更新时间:2023-12-04 17:16:50 33 4
gpt4 key购买 nike

我正在使用 kivy & kivymd,我正在尝试实现一种最佳方式,将高斯模糊应用于作为背景图像的 FitImage

自然地,我尝试了 EffectWidget,但是,这不是最佳考虑到我为其设置动画并且我希望用户能够调整窗口大小和根据文档,每次小部件发生更改时,effectwidget 都会重新创建 fbo。

我使用的代码:

from kivy.lang import Builder
from kivymd.app import MDApp


class MainApp(MDApp):
def __init__(self, **kwargs):
super(MainApp, self).__init__(**kwargs)
self.kv = Builder.load_string('''
#:kivy 2.0.0
#:import ew kivy.uix.effectwidget
EffectWidget:
effects: ew.HorizontalBlurEffect(size=12.0), ew.VerticalBlurEffect(size=12.0)
FitImage:
source: "images/song_img.jpg"
# the rest of my code...
''')

def build(self):
return self.kv


if __name__ == '__main__':
MainApp().run()

所以我认为实现我想要的唯一合适的方法是更改​​ glsl 代码

ew.Horizo​​ntalBlurEffect 的 glsl 代码:

effect_blur_h = '''
vec4 effect(vec4 color, sampler2D texture, vec2 tex_coords, vec2 coords)
{{
float dt = ({} / 4.0) * 1.0 / resolution.x;
vec4 sum = vec4(0.0);
sum += texture2D(texture, vec2(tex_coords.x - 4.0*dt, tex_coords.y))
* 0.05;
sum += texture2D(texture, vec2(tex_coords.x - 3.0*dt, tex_coords.y))
* 0.09;
sum += texture2D(texture, vec2(tex_coords.x - 2.0*dt, tex_coords.y))
* 0.12;
sum += texture2D(texture, vec2(tex_coords.x - dt, tex_coords.y))
* 0.15;
sum += texture2D(texture, vec2(tex_coords.x, tex_coords.y))
* 0.16;
sum += texture2D(texture, vec2(tex_coords.x + dt, tex_coords.y))
* 0.15;
sum += texture2D(texture, vec2(tex_coords.x + 2.0*dt, tex_coords.y))
* 0.12;
sum += texture2D(texture, vec2(tex_coords.x + 3.0*dt, tex_coords.y))
* 0.09;
sum += texture2D(texture, vec2(tex_coords.x + 4.0*dt, tex_coords.y))
* 0.05;
return vec4(sum.xyz, color.w);
}}
'''

根据文档,AdvancedEffectBase 可以帮助解决这些问题,但是,问题是我不知道如何更改这些 glsl 代码以实现我想要的。

我尝试使用其他人的 glsl 代码来应用像这样的高斯模糊效果:

Shadertoy's code for gaussian blur effect

Mattdesl's code from github

和其他人...

我应该如何实现我想要的?

更新:@Tshirtman 的回答似乎是迄今为止最好的回答,但是,我个人遇到了问题。

我在单独的屏幕中使用模糊图像,图像似乎没有跟随屏幕管理器的过渡动画,而是在其他小部件慢慢到位时显示出来。这是可以修复的吗?另外,有没有办法提高模糊的分辨率?看起来有点低分辨率。

我的代码:

from kivy.lang import Builder
from kivy.core.window import Window
from kivy.graphics import RenderContext
from kivymd.utils.fitimage import FitImage
from kivymd.app import MDApp


class BlurredBackgroundImage(FitImage):
def __init__(self, **kwargs):
fs = '''
$HEADER$

uniform vec2 resolution;

void main(void) {
int radius = 4;
vec2 d = float(radius) / resolution;
for (int dx = -radius; dx < radius; dx++)
for (int dy = -radius; dy < radius; dy++)
gl_FragColor += texture2D(texture0, tex_coord0 + vec2(float(dx), float(dy)) * d);

gl_FragColor /= float( 4 * radius * radius);
}
'''
self.canvas = RenderContext()
self.canvas.shader.fs = fs
super(BlurredBackgroundImage, self).__init__(**kwargs)

def on_size(self, *args):
self.canvas['projection_mat'] = Window.render_context['projection_mat']
self.canvas['modelview_mat'] = Window.render_context['modelview_mat']
self.canvas['resolution'] = list(map(float, self.size))
print("size changed")

# tried updating the shader whenever the position changes but still no improvements
'''def on_pos(self, *args):
self.canvas['projection_mat'] = Window.render_context['projection_mat']
self.canvas['modelview_mat'] = Window.render_context['modelview_mat']
self.canvas['resolution'] = list(map(float, self.size))
print("pos changed")'''


class MainApp(MDApp):
def __init__(self, **kwargs):
super(MainApp, self).__init__(**kwargs)
self.kv = Builder.load_string('''
ScreenManager:
Screen:
name: "main-menu"
Button:
text: "Go to next Page!"
on_release:
root.transition.direction = "down"
root.current = "song-view"
Screen:
name: "song-view"
RelativeLayout:
BlurredBackgroundImage:
source: "images/song_img.jpg"
Button:
text: "Return to main menu!"
size_hint: .25, .25
pos_hint: {"center_x": .5, "center_y": .5}
on_release:
root.transition.direction = "up"
root.current = "main-menu"
''')

def build(self):
return self.kv


if __name__ == '__main__':
MainApp().run()

最佳答案

如果你不想使用 FBO,我认为你不应该使用 EffectWidget,而是直接在 RenderContext 上创建你的着色器,并将你想用作背景的纹理绑定(bind)到它,这样着色器就可以在其中查找像素

存储库中有一个多纹理示例,我们可以将其用作起点

https://github.com/kivy/kivy/blob/c28c47b39ae57c97c70cc0398d74774d73a6894b/examples/canvas/multitexture.py

我会这样想(未经测试)。

from kivy.clock import Clock
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.graphics import RenderContext


fs_multitexture = '''
$HEADER$

uniform vec2 resolution;

void main(void) {
int radius = 4;
vec2 d = float(radius) / resolution;
for (int dx = -radius; dx < radius; dx++)
for (int dy = -radius; dy < radius; dy++)
gl_FragColor += texture2D(texture0, tex_coord0 + vec2(float(dx), float(dy)) * d);

gl_FragColor /= float( 4 * radius * radius);
}
'''


kv = """
<BackgroundBluredImage>:
canvas:
Color:
Rectangle:
# the shader will apply where the widget draws, so we need a rectangle of the appropriate size/pos
pos: self.pos
size: self.size
# binding the texture to it so we can look it up in the shader and do our thing
source: "Coast.jpg"

FloatLayout:
BackgroundBluredImage:
"""


class BackgroundBluredImage(Widget):
def __init__(self, **kwargs):
self.canvas = RenderContext()
self.canvas.shader.fs = fs_multitexture
super().__init__(**kwargs)
# We'll update our glsl variables in a clock to run every frame, but you could bind it to relevant events only, like size and pos
Clock.schedule_interval(self.update_glsl, 0)

def update_glsl(self, *largs):
# This is needed for the default vertex shader.
self.canvas['projection_mat'] = Window.render_context['projection_mat']
self.canvas['modelview_mat'] = Window.render_context['modelview_mat']
self.canvas['resolution'] = list(map(float, self.size))


class BluredBackgroundApp(App):
def build(self):
return Builder.load_string(kv)


if __name__ == '__main__':
BluredBackgroundApp().run()

关于python - 在kivy中实现最优高斯模糊效果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68527507/

33 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com