Windows Phone 7 Custom Message Box

本文介绍了一个用于Windows Phone 7的通知框组件NotificationBox,它提供了自定义按钮、重复显示消息等功能,简化了用户界面的实现。


UPDATE: Notification box has been updated: Take a look at http://wpassets.codeplex.com, the new home place for NotificationBox and others.

I’ve wrote a custom message box called NotificationBox for the Windows Phone 7. It gives you an option to pick whatever button you like to have inside the message box. Also It provides a cool feature which is ‘Show this message again’ with isolated settings persistency so you won’t have to create this mechanism by yourself each time you want to give an option to the user suppressing a message.

The usage is similar to the regular message box, except for the fact that you can pass commands with custom actions instead of return values – then switch/ifs.

For example:

NotificationBox.Show(
                "Exit",
                "Are you sure?",
                new NotificationBoxCommand("Yes", () => { }),
                new NotificationBoxCommand("No", () => { }));
NotificationBox.Show(
                "Erase",
                "You are about to loose your data!",
                new NotificationBoxCommand("Ok", () => { }),
                new NotificationBoxCommand("Cancel", () => { }));
NotificationBox.Show(
                "Choose",
                "Choose an option.",
                new NotificationBoxCommand("Save", () => { }),
                new NotificationBoxCommand("Load", () => { }));
NotificationBox.Show(
                "Custom",
                "Click on any of the buttons below.",
                new NotificationBoxCommand("Xxx", () => { }),
                new NotificationBoxCommand("Xxx", () => { }),
                new NotificationBoxCommand("Zzz", () => { }));

Yields:

image image image image

Another version of the Show method called ShowAgain, gives an option to suppress a message:

NotificationBox.ShowAgain(
                "Show Again",
                "Uncheck the show again message and this message won't appear again.",
                "Show this message again",
                false,
                suppressed => { },
                GetType().ToString(),                
                new NotificationBoxCommand("Xxx", () => { }),
                new NotificationBoxCommand("Xxx", () => { }),
                new NotificationBoxCommand("Zzz", () => { }));
NotificationBox.ShowAgain(
                "Show Again",
                "Check the show again message so this message will appear again.",
                "Show this message again",
                true,
                suppressed => { },
                GetType().ToString(),                
                new NotificationBoxCommand("Xxx", () => { }),
                new NotificationBoxCommand("Xxx", () => { }),
                new NotificationBoxCommand("Zzz", () => { }));

The first call with false, displays the following message:

image

Now calling the same method again with the same parameters (at least with GetType().ToString()), won’t open the message in case that the user suppressed it by unchecking the check box.

The second snippet (fourth param is true) forces the message to open event if suppressed, but now the same message appears and the checkbox left unchecked. The user have an option to check it again.

I’ll be happy to have inputs, so please – don’t be shy ;)

You can download the source from CodePlex.

<script setup lang="ts"> import {ref, nextTick, computed} from 'vue' import OpenAI from "openai"; definePage({ name: 'AI', style: { navigationBarTitleText: 'AI', navigationStyle: 'custom' }, }) // 弹出框 const show = ref(false) const onClickLeft = () => { uni.navigateBack() } // ai 模型选择 const aiForm = ref({ model: 'DeepSeek', iconUrl: '/static/AI/deepseek.png', width: 35, height: 22, }) // ai 模型列表 const aiModel = ref([{ id: 1, name: 'DeepSeek V3.2 Think', iconUrl: '/static/AI/deepseek.png', width: 35, height: 22, }, { id: 2, name: 'DeepSeek V3.1 Think', iconUrl: '/static/AI/deepseek.png', width: 35, height: 22, }, { id: 3, name: 'ERNIE 4.5 Turbo VL', iconUrl: '/static/AI/ernie.png', width: 22, height: 22, }, { id: 4, name: 'ERNIE X1.1', iconUrl: '/static/AI/ernie.png', width: 22, height: 22, }]) // 选择模型 const selectModel = () => { show.value = true } // 变更模型 const changeModel = (value: any) => { aiForm.value.model = value.name aiForm.value.iconUrl = value.iconUrl aiForm.value.width = value.width aiForm.value.height = value.height show.value = false } // 聊天消息列表 const messages = ref<any>([ {role: 'ai', content: '你好!我是你的 AI 助手,请问有什么可以帮您?'} ]) // 输入框内容 const inputMessage = ref('') // 是否正在加载 AI 回复 const loading = ref(false) // 自动滚动到最新消息 const scrollIntoView = computed(() => { return `msg-${messages.value.length - 1}` }) // 发送消息 // const handleSend = async () => { // if (!inputMessage.value.trim() || loading.value) return // // // 添加用户消息 // messages.value.push({ // role: 'user', // content: inputMessage.value // }) // inputMessage.value = '' // // // 显示加载状态 // loading.value = true // // // 确保视图更新后滚动到底部 // await nextTick() // await scrollToBottom() // // // 模拟调用 AI 接口 // try { // const response = await callAiApi(messages.value.slice(-1)[0].content) // messages.value.push({ // role: 'ai', // content: response // }) // } catch (error) { // messages.value.push({ // role: 'ai', // content: '抱歉,AI 服务暂时不可用,请稍后再试。' // }) // } finally { // loading.value = false // await nextTick() // await scrollToBottom() // } // } const handleSend = async () => { if (!inputMessage.value.trim() || loading.value) return // 添加用户消息 messages.value.push({ role: 'user', content: inputMessage.value }) // 清空输入框 inputMessage.value = '' // 显示加载状态 & 添加空 AI 消息占位 loading.value = true messages.value.push({ role: 'ai', content: '' }) await nextTick() await scrollToBottom() try { await callAiApi(messages.value[messages.value.length - 2].content) } catch (error: any) { const lastMsg = messages.value[messages.value.length - 1] lastMsg.content = `抱歉,AI 服务出错:${error.message || '未知错误'}` } finally { loading.value = false await nextTick() await scrollToBottom() } } // AI 接口请求 // const callAiApi = (prompt: any) => { // return new Promise((resolve, reject) => { // uni.request({ // url: 'https://api.deepseek.com/chat/completions', // method: 'POST', // header: { // 'Authorization': `Bearer ` + `${import.meta.env.VITE_ENV_NAME}`, // 'Content-Type': 'application/json', // }, // data: {prompt}, // success: (res) => { // if (res.statusCode === 200) { // resolve(res.data.response) // } else { // reject(new Error('AI request failed')) // } // }, // fail: reject // }) // }) // } // AI 接口请求 - 流式输出版本 const callAiApi = async (prompt: string) => { return new Promise<string>((resolve, reject) => { const abortController = new AbortController(); fetch('https://api.deepseek.com/v1/chat/completions', { method: 'POST', headers: { 'Authorization': `Bearer ${import.meta.env.VITE_DEEPSEEK_API_KEY}`, 'Content-Type': 'application/json', 'Accept': 'text/event-stream' }, body: JSON.stringify({ model: 'deepseek-chat', messages: [ {role: 'user', content: prompt} ], stream: true // 启用流式输出 }), signal: abortController.signal }) .then((response) => { if (!response.ok || !response.body) { throw new Error(`HTTP error! status: ${response.status}`); } const reader = response.body!.getReader(); const decoder = new TextDecoder('utf-8'); let accumulatedText = ''; const readChunk = (): void => { reader.read().then(({done, value}) => { if (done) { resolve(accumulatedText); // 完整内容 resolve return; } const chunk = decoder.decode(value, {stream: true}); const lines = chunk.split('\n'); for (const line of lines) { if (line.startsWith('data: ') && line !== 'data: [DONE]') { try { const json = JSON.parse(line.slice(6)); // 去掉 "data: " const text = json.choices[0]?.delta?.content || ''; accumulatedText += text; // 实时更新最后一条 AI 消息的内容 const lastMsg = messages.value[messages.value.length - 1]; if (lastMsg.role === 'ai') { lastMsg.content = accumulatedText; } // 强制滚动到底部 nextTick(() => { scrollToBottom(); }); } catch (e) { console.warn('Parse error:', e); } } } readChunk(); // 继续读取下一块 }).catch((err) => { reject(err); }); }; readChunk(); }) .catch((err) => { if (err.name === 'AbortError') return; reject(err); }); }); }; // 滚动到底部 const scrollToBottom = () => { return nextTick() } // 键盘高度状态 const keyboardHeight = ref(0) onMounted(() => { // 监听键盘高度变化 uni.onKeyboardHeightChange((res) => { keyboardHeight.value = res.height || 0 nextTick(() => { scrollToBottom() }) }) }) onBeforeUnmount(() => { uni.offKeyboardHeightChange() }) // 弹出框关闭时清空 const handleClose = () => { show.value = false } </script> <template> <view class="container"> <wd-navbar custom-class="ai-navbar" fixed custom-style="background-color: transparent !important; height: 140rpx !important;" safeAreaInsetTop> <template #left> <view class="left-button" @click="onClickLeft"> <wd-icon name="arrow-left1" size="22px"></wd-icon> </view> </template> <template #title> <view class="title-box" @click="selectModel"> <wd-img :width="aiForm.width" :height="aiForm.height" :src="aiForm.iconUrl"/> <span class="title-text">{{ aiForm.model }}</span> <wd-icon name="caret-down-small" size="18px"></wd-icon> </view> </template> </wd-navbar> <view style="height: calc(140rpx + 54px)"> </view> <view class="chat-container"> <!-- 消息列表 --> <scroll-view class="message-list" :scroll-y="true" :scroll-into-view="scrollIntoView" > <!-- 单条消息 --> <view v-for="(msg, index) in messages" :key="index" :id="'msg-' + index" :class="['message-item', msg.role === 'user' ? 'message-item-user' : 'message-item-ai']" > <!-- 用户消息:右对齐,带头像 --> <view v-if="msg.role === 'user'" class="user-message-container"> <text class="user-message-text">{{ msg.content }}</text> <image src="/static/user-avatar.png" class="avatar"/> </view> <!-- AI 消息:左对齐,带头像 --> <view v-else class="ai-message"> <image src="/static/avatar.png" class="avatar"/> <text class="ai-message-text">{{ msg.content }}</text> </view> </view> <!-- AI 正在输入状态 --> <view v-if="loading" class="ai-message"> <image src="/static/avatar.png" class="avatar"/> <text class="message-text">AI思考中...</text> </view> </scroll-view> <!-- 输入区域 --> <view class="input-area" :style="{ bottom: `${keyboardHeight}px`, paddingBottom: 'env(safe-area-inset-bottom)' }"> <input v-model="inputMessage" class="input-field" type="text" placeholder="请输入您的问题..." @confirm="handleSend" :adjust-position="false" > </input> </view> </view> </view> <wd-popup v-model="show" position="top" transition="fade-down" custom-style="border-radius:32rpx;top:200rpx;left:40rpx;right:40rpx;padding-bottom: 30rpx;" @close="handleClose"> <wd-card title="模型选择" class="model-container"> <view v-for="item in aiModel" :key="item.id" class="model-item" @click="changeModel(item)"> <wd-img :width="item.width" :height="item.height" :src="item.iconUrl"/> <span class="title-text">{{ item.name }}</span> </view> </wd-card> </wd-popup> </template> <style scoped lang="scss"> :deep(.wd-navbar.is-border:after) { background: none !important; } .left-button { border-radius: 50px; height: 80rpx; width: 80rpx; background-color: rgba(172, 172, 172, 0.13); display: flex; align-items: center; justify-content: center; } .title-box { display: flex; height: 100%; align-items: center; justify-content: center; } .title-text { margin-left: 10rpx; font-size: 28rpx; } .model-container { width: 500rpx; height: 460rpx; display: flex; justify-content: center; align-items: center; font-size: 40rpx; border-radius: 32rpx; } .model-item { height: 100rpx; border-radius: 15rpx; margin-bottom: 20rpx; border: #9e56f6 1rpx solid; display: flex; align-items: center; justify-content: center; } :deep(.page-wraper) { min-height: 0 !important; } /* 聊天区域 根容器:全屏高度,弹性布局垂直排列 */ .chat-container { display: flex; flex-direction: column; width: 100%; height: calc(100vh - 140rpx - 54px); overflow-x: hidden; box-sizing: border-box; } /* 消息列表区域:自动填充剩余空间 */ .message-list { flex: 1; /* 自动伸缩以填满父容器减去输入框的高度 */ padding: 20rpx; overflow-y: auto; /* 垂直滚动 */ overflow-x: hidden; /* 防止意外横向滚动 */ box-sizing: border-box; } /* 每一条消息容器 */ .message-item { display: flex; margin-bottom: 15rpx; width: 100%; box-sizing: border-box; } /* 每一条消息容器 */ .message-item { display: flex; margin-bottom: 25rpx; width: 100%; box-sizing: border-box; } /* 用户消息整体右对齐 */ .message-item-user { justify-content: flex-end; /* 关键:让整条消息靠右 */ } /* AI 消息左对齐 */ .message-item-ai { justify-content: flex-start; } /* 用户消息容器(仅内容排列)*/ .user-message-container { display: flex; align-items: flex-start; gap: 16rpx; max-width: 100%; justify-content: flex-end; /* 整体右对齐 */ } /* 用户消息气泡 */ .user-message-text { padding: 16rpx 24rpx; border-radius: 30rpx 0 30rpx 30rpx; /* 右上角无弧度 */ max-width: 70%; word-wrap: break-word; word-break: break-word; font-size: 28rpx; box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.05); line-height: 1.5; background-image: linear-gradient(rgba(0, 222, 255, 0.2), rgba(101, 0, 255, 0.2)); } /* AI 消息气泡 */ .ai-message-text { padding: 16rpx 24rpx; border-radius: 0 30rpx 30rpx 30rpx; /* 左下角无弧度 */ max-width: 70%; word-wrap: break-word; word-break: break-word; font-size: 28rpx; box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.05); line-height: 1.5; background-image: linear-gradient(rgba(2, 255, 216, 0.2), rgba(255, 0, 221, 0.2)); } /* AI 消息整体容器:左对齐,含头像和文本 */ .ai-message { display: flex; align-items: flex-start; gap: 16rpx; /* 头像与文字间距 */ max-width: 100%; } /* AI 头像样式 */ .avatar { width: 60rpx; height: 60rpx; border-radius: 50%; /* 圆形 */ border: #b559ff 1rpx solid; flex-shrink: 0; /* 防止被压缩 */ } /* 输入区域:固定在底部,留出安全区 */ .input-area { display: flex; align-items: center; padding: 20rpx; position: relative; z-index: 999; width: 100%; box-sizing: border-box; /* 安全区适配:iOS 设备底部留白 */ padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx); /* 或使用 constant,在老版本 iOS 中兼容 */ padding-bottom: calc(constant(safe-area-inset-bottom) + 20rpx); padding-bottom: calc(env(safe-area-inset-bottom, 20rpx) + 20rpx); } /* 输入框样式(可自由调整) */ .input-field { flex: 1; height: 120rpx; line-height: 80rpx; border: 1rpx solid #da34ff; border-radius: 25rpx; padding: 0 30rpx; font-size: 28rpx; max-width: 100%; margin-right: 20rpx; overflow: hidden; word-break: break-word; margin-bottom: 10rpx; } </style> 照着这个样式重新生成优化,聊天区域的高度要跟随键盘的高度改变,
11-09
<script setup lang="ts"> import {ref, nextTick, computed} from 'vue' definePage({ name: 'AI', style: { navigationBarTitleText: 'AI', navigationStyle: 'custom' }, }) // 弹出框 const show = ref(false) const onClickLeft = () => { uni.navigateBack() } // ai 模型选择 const aiForm = ref({ model: 'DeepSeek', iconUrl: '/static/AI/deepseek.png', width: 35, height: 22, }) // ai 模型列表 const aiModel = ref([{ id: 1, name: 'DeepSeek V3.2 Think', iconUrl: '/static/AI/deepseek.png', width: 35, height: 22, }, { id: 2, name: 'DeepSeek V3.1 Think', iconUrl: '/static/AI/deepseek.png', width: 35, height: 22, }, { id: 3, name: 'ERNIE 4.5 Turbo VL', iconUrl: '/static/AI/ernie.png', width: 22, height: 22, }, { id: 4, name: 'ERNIE X1.1', iconUrl: '/static/AI/ernie.png', width: 22, height: 22, }]) // 选择模型 const selectModel = () => { show.value = true } // 变更模型 const changeModel = (value: any) => { aiForm.value.model = value.name aiForm.value.iconUrl = value.iconUrl aiForm.value.width = value.width aiForm.value.height = value.height show.value = false } // 聊天消息列表 const messages = ref<any>([ {role: 'ai', content: '你好!我是你的 AI 助手,请问有什么可以帮您?'} ]) // 输入框内容 const inputMessage = ref('') // 是否正在加载 AI 回复 const loading = ref(false) // 自动滚动到最新消息 const scrollIntoView = computed(() => { return `msg-${messages.value.length - 1}` }) // 发送消息 const handleSend = async () => { if (!inputMessage.value.trim() || loading.value) return // 添加用户消息 messages.value.push({ role: 'user', content: inputMessage.value }) inputMessage.value = '' // 显示加载状态 loading.value = true // 确保视图更新后滚动到底部 await nextTick() scrollToBottom() // 模拟调用 AI 接口 try { const response = await callAiApi(messages.value.slice(-1)[0].content) messages.value.push({ role: 'ai', content: response }) } catch (error) { messages.value.push({ role: 'ai', content: '抱歉,AI 服务暂时不可用,请稍后再试。' }) } finally { loading.value = false await nextTick() scrollToBottom() } } // 模拟 AI 接口请求(实际项目中替换为真实 API) const callAiApi = (prompt: any) => { return new Promise((resolve) => { setTimeout(() => { resolve(`这是对“${prompt}”的回答。AI正在模拟中...`) }, 1200) }) // 实际开发中应使用如下方式: // return new Promise((resolve, reject) => { // uni.request({ // url: 'https://your-ai-api.com/chat', // method: 'POST', // data: {prompt}, // success: (res) => { // if (res.statusCode === 200) { // resolve(res.data.response) // } else { // reject(new Error('AI request failed')) // } // }, // fail: reject // }) // }) } // 滚动到底部 const scrollToBottom = () => { return nextTick() } // 弹出框关闭时清空 const handleClose = () => { show.value = false } </script> <template> <wd-navbar custom-class="ai-navbar" fixed custom-style="background-color: transparent !important; height: 140rpx !important;" safeAreaInsetTop> <template #left> <view class="left-button" @click="onClickLeft"> <wd-icon name="arrow-left1" size="22px"></wd-icon> </view> </template> <template #title> <view class="title-box" @click="selectModel"> <wd-img :width="aiForm.width" :height="aiForm.height" :src="aiForm.iconUrl"/> <span class="title-text">{{ aiForm.model }}</span> <wd-icon name="caret-down-small" size="18px"></wd-icon> </view> </template> </wd-navbar> <view class="container"> <view class="chat-container"> <!-- 消息列表 --> <scroll-view class="message-list" scroll-y :scroll-into-view="scrollIntoView" > <!-- 单条消息 --> <view v-for="(msg, index) in messages" :key="index" :id="'msg-' + index" class="message-item" > <!-- 用户消息:右对齐 --> <view v-if="msg.role === 'user'" class="user-message"> <text class="message-text">{{ msg.content }}</text> </view> <!-- AI 消息:左对齐,带头像 --> <view v-else class="ai-message"> <image src="/static/avatar.png" class="avatar"/> <text class="message-text">{{ msg.content }}</text> </view> </view> <!-- AI 正在输入状态 --> <view v-if="loading" class="ai-message"> <image src="/static/ai-avatar.png" class="avatar"/> <text class="message-text">AI思考中...</text> </view> </scroll-view> <!-- 输入区域 --> <view class="input-area"> <input v-model="inputMessage" class="input-field" type="text" placeholder="请输入您的问题..." @confirm="handleSend" /> <!-- <wd-input type="text" v-model="inputMessage" placeholder="请输入用户名" @change="handleSend"/>--> <!-- <button class="send-btn" size="mini" type="primary" @click="handleSend">发送</button>--> </view> </view> </view> <wd-popup v-model="show" position="top" transition="fade-down" custom-style="border-radius:32rpx;top:200rpx;left:40rpx;right:40rpx;padding-bottom: 30rpx;" @close="handleClose"> <wd-card title="模型选择" class="model-container"> <view v-for="item in aiModel" :key="item.id" class="model-item" @click="changeModel(item)"> <wd-img :width="item.width" :height="item.height" :src="item.iconUrl"/> <span class="title-text">{{ item.name }}</span> </view> </wd-card> </wd-popup> </template> <style scoped lang="scss"> :deep(.wd-navbar.is-border:after) { background: none !important; } .left-button { border-radius: 50px; height: 80rpx; width: 80rpx; background-color: rgba(172, 172, 172, 0.13); display: flex; align-items: center; justify-content: center; } .title-box { display: flex; height: 100%; align-items: center; justify-content: center; } .title-text { margin-left: 10rpx; font-size: 28rpx; } .model-container { width: 500rpx; height: 460rpx; display: flex; justify-content: center; align-items: center; font-size: 40rpx; border-radius: 32rpx; } .model-item { height: 100rpx; border-radius: 15rpx; margin-bottom: 20rpx; border: #9e56f6 1rpx solid; display: flex; align-items: center; justify-content: center; } .container { margin-top: calc(140rpx + 54px); } :deep(.page-wraper) { min-height: 0 !important; } /* 聊天区域 */ /* 根容器:全屏高度,弹性布局垂直排列 */ .chat-container { display: flex; flex-direction: column; width: 100%; height: calc(100vh - 140rpx - 54px); background-color: #f5f5f5; overflow-x: hidden; box-sizing: border-box; } /* 消息列表区域:自动填充剩余空间 */ .message-list { flex: 1; /* 自动伸缩以填满父容器减去输入框的高度 */ padding: 20rpx; overflow-y: auto; /* 垂直滚动 */ overflow-x: hidden; /* 防止意外横向滚动 */ box-sizing: border-box; } /* 每一条消息容器 */ .message-item { display: flex; margin-bottom: 15rpx; width: 100%; /* 防止溢出 */ box-sizing: border-box; } /* 用户消息气泡:右对齐 */ .user-message { margin-left: auto; /* 推到右侧 */ color: white; padding: 16rpx 24rpx; border-radius: 30rpx 30rpx 0 30rpx; /* 右下角无弧度 */ max-width: 70%; /* 最大宽度限制 */ word-wrap: break-word; /* 自动换行 */ word-break: break-word; /* 强制拆分长单词 */ font-size: 28rpx; box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.1); } /* AI 消息整体容器:左对齐,含头像和文本 */ .ai-message { display: flex; align-items: flex-start; gap: 16rpx; /* 头像与文字间距 */ max-width: 100%; } /* AI 头像样式 */ .avatar { width: 60rpx; height: 60rpx; border-radius: 50%; /* 圆形 */ background-color: #ddd; flex-shrink: 0; /* 防止被压缩 */ } /* 所有消息文本统一类名 */ .message-text { background-color: white; padding: 16rpx 24rpx; border-radius: 30rpx 30rpx 30rpx 0; /* 左下角无弧度 */ max-width: 70%; word-wrap: break-word; word-break: break-word; font-size: 28rpx; color: #333; box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.05); line-height: 1.5; } /* 输入区域:固定在底部,留出安全区 */ .input-area { display: flex; align-items: center; padding: 20rpx; position: relative; z-index: 999; width: 100%; box-sizing: border-box; /* 安全区适配:iOS 设备底部留白 */ padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx); /* 或使用 constant,在老版本 iOS 中兼容 */ padding-bottom: calc(constant(safe-area-inset-bottom) + 20rpx); padding-bottom: calc(env(safe-area-inset-bottom, 20rpx) + 20rpx); } /* 输入框样式(可自由调整) */ .input-field { flex: 1; height: 150rpx; line-height: 80rpx; border: 1rpx solid #da34ff; border-radius: 25rpx; padding: 0 30rpx; font-size: 28rpx; color: #333; max-width: 100%; margin-right: 20rpx; overflow: hidden; word-break: break-word; } /* 发送按钮 */ .send-btn { width: 120rpx; height: 80rpx; font-size: 28rpx; background-color: #007aff; color: white; border-radius: 40rpx; display: flex; align-items: center; justify-content: center; flex-shrink: 0; /* 防止被挤压 */ } </style> 添加用户头像,用户消息用一层气泡,不要使用多层,输入框输入后由键盘的完成键发送消息
11-07
### Box Filter 的定义与应用 Box Filter 是一种常见的滤波器,在计算机视觉和图像处理领域被广泛应用于平滑化操作以及降噪。它通过计算局部区域像素值的平均来达到模糊效果,其核心思想是对输入图像中的每一个像素点取一个固定大小的邻域窗口,并对该窗口内的所有像素求均值。 在实际实现中,Box Filter 可以看作是一种特殊的卷积核(Convolutional Kernel),其中所有的权重均为相等的常数[^1]。这种特性使得它的计算非常高效,尤其适合实时应用场景下的快速预处理需求。 #### 使用 TensorFlow/Keras 实现 Box Filter 基于 Keras 提供的功能可以轻松构建并应用 Box Filter 到图像数据上。下面是一个简单的例子展示如何利用 `Keras` 中的 `Conv2D` 层模拟 Box Filter: ```python import numpy as np from tensorflow import keras def create_box_filter(size): """ 创建一个指定尺寸的盒状滤波器 """ filter_weights = np.ones((size, size)) / (size * size) return filter_weights[..., None] box_kernel = create_box_filter(3) # 定义一个 3x3 大小的 box filter input_shape = (None, 64, 64, 1) # 假设输入图片为灰度图,分辨率为 64x64 model = keras.models.Sequential([ keras.layers.InputLayer(input_shape=input_shape[1:]), keras.layers.DepthwiseConv2D( kernel_size=3, depth_multiplier=1, strides=1, padding="same", use_bias=False, weights=[box_kernel], name="box_filter_layer" ) ]) # 测试模型 test_image = np.random.rand(*input_shape).astype(np.float32) output = model.predict(test_image) print("Input shape:", test_image.shape) print("Output shape:", output.shape) ``` 上述代码片段展示了如何创建一个自定义的 Box Filter 并将其作为卷积层的一部分嵌入到神经网络架构之中[^2]。这里需要注意的是,我们手动设置了卷积核参数而不是让它们成为可训练变量,这正是为了保持 Box Filter 的原始功能不变形。 #### 总结 Box Filters 虽然简单却十分有效,尤其是在需要降低噪声或者减少高频细节的时候尤为适用。然而由于缺乏方向性和频率选择能力,对于更复杂的特征提取任务来说可能不够灵活,因此通常会与其他类型的滤波器配合使用,比如高斯滤波器(Gaussian Blur) 或者 Sobel 边缘检测算子等等。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值