当前位置: 首页 > news >正文

自适应阈值二值化参数详解 ,计算机视觉,图片处理 邻域大小 调整常数(C=3)和可视化调节参数的应用程序

自适应阈值二值化参数详解

函数参数分析:

cv2.adaptiveThreshold(
filtered, # 输入图像(需为单通道灰度图)
255, # 二值化后的最大值(白色)
cv2.ADAPTIVE_THRESH_GAUSSIAN_C, # 自适应方法
cv2.THRESH_BINARY_INV, # 阈值化模式(反向二值化)
block_size=9, # 邻域大小
C=3 # 阈值调整常数
)

关键参数解释:

  1. 邻域大小(block_size=9)
    • 物理含义:每个像素点的阈值由其周围 9×9像素区域 计算得出

    • 为什么是奇数?:必须为奇数(3,5,7,…)确保有中心像素点

  2. 调整常数(C=3)
    • 计算方式:最终阈值 = 邻域计算值 - C

    • 作用:降低阈值门槛,使更多像素被判定为前景(白线)

自适应过程详解(以像素点P为例):

  1. 邻域统计:取P点周围9×9区域像素值
    !https://i.imgur.com/7GtTj4L.png

  2. 计算阈值:
    ⚙️ 使用高斯加权法计算邻域阈值(中心权重最高,边缘最低)
    Tlocal=∑(x,y)∈9×9(wxy⋅I(x,y))−C T_{local} = \sum_{(x,y) \in 9×9} (w_{xy} \cdot I(x,y)) - \text{C} Tlocal=(x,y)9×9(wxyI(x,y))C

    其中w_{xy}为高斯分布生成的权重

  3. 反向二值化判断:
    if 像素值 > T_local:
    输出 = 0 # 黑色背景
    else:
    输出 = 255 # 白色线状目标

使用场景示例:

参数组合 适用场景

block_size=9, C=3 中等噪声环境下的文本/线条检测

block_size=3, C=0 高清图像的精细边缘提取

block_size=15, C=5 光照严重不均的工业图像

效果对比:

!https://i.imgur.com/mQCbD3k.gif
注:增大block_size可抗噪但降低细节敏感度,增大C可增强线条连续性

最佳实践:通常设置block_size为图像中目标线宽的35倍,C值取灰度动态范围的35%(如8位图取2~12)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

pip install --upgrade --extra-index-url https://pysimplegui.net/install PySimpleGUI
pip install opencv-python numpy pillow

可视化调节参数的应用程序

当然还有 ,ipynb文件格式的

import cv2
import numpy as np
import PySimpleGUI as sg
from PIL import Image
import io
import os
import sys
import tracebackdef resource_path(relative_path):""" 解决资源路径问题 """try:base_path = sys._MEIPASSexcept Exception:base_path = os.path.abspath(".")return os.path.join(base_path, relative_path)# 创建参数调节界面
def create_gui():try:sg.theme('LightGrey1')except:sg.theme('LightGray1')# 布局定义 - 确保原始图在左,结果在右layout = [[sg.Text('图片:'),sg.Input(key='-FILEPATH-', enable_events=True, visible=False),sg.FileBrowse('选择图片', target='-FILEPATH-', file_types=(("图片文件", "*.png;*.jpg;*.jpeg;*.bmp"),)),sg.Button('重置参数', key='-RESET-'),sg.Button('保存结果', key='-SAVE-'),sg.Button('退出', key='-EXIT-')],[sg.Frame('原始图像', [[sg.Image(key='-ORIGINAL-', size=(400, 300))]], size=(410, 330)),sg.Frame('二值化结果', [[sg.Image(key='-RESULT-', size=(400, 300))]], size=(410, 330))],[sg.Text('邻域大小 (block_size): '),sg.Slider(range=(3, 41), default_value=9, resolution=2, orientation='h',key='-BLOCK_SIZE-', size=(30, 20)),sg.Text('9 (奇数)', key='-BLOCK_SIZE_TXT-', size=(10, 1))],[sg.Text('调整常数 (C): '),sg.Slider(range=(-20, 20), default_value=3, resolution=1, orientation='h',key='-C-', size=(30, 20)),sg.Text('3', key='-C_TXT-', size=(10, 1))],[sg.Text('阈值模式:'),sg.Combo(['高斯加权', '平均加权'], default_value='高斯加权', key='-METHOD-'),sg.Text('输出模式:'),sg.Combo(['白线黑底', '黑线白底'], default_value='白线黑底', key='-OUTPUT-MODE-'),sg.Button('应用参数', key='-APPLY-', button_color=('white', 'blue'))],[sg.Multiline("参数指南:\n""1. 邻域大小(block_size): 必须是奇数,影响细节保留能力\n""   - 值越大,抗噪能力越强,但可能模糊细节\n""   - 值越小,细节保留越好,但容易引入噪声\n\n""2. 调整常数(C): 控制阈值的敏感度\n""   - 正值:降低阈值,保留更多细节\n""   - 负值:提高阈值,抑制噪点\n\n""3. 阈值模式选择:\n""   - 高斯加权:考虑像素位置权重(推荐)\n""   - 平均加权:快速计算平均阈值\n\n""推荐参数:\n""- 清晰图像: block_size=3-9, C=1-5\n""- 有噪图像: block_size=11-21, C=-3到0",size=(100, 12), disabled=True, background_color='#F0F0F0')]]# 创建窗口return sg.Window('自适应阈值二值化参数调节工具', layout, finalize=True, resizable=True)# 支持中文路径的图像读取函数
def imread_chinese_path(filepath):"""使用cv2.imdecode读取中文路径图片"""try:with open(filepath, "rb") as stream:bytes_data = bytearray(stream.read())numpyarray = np.asarray(bytes_data, dtype=np.uint8)img = cv2.imdecode(numpyarray, cv2.IMREAD_UNCHANGED)return imgexcept Exception as e:sg.popup_error(f"读取图片错误: {str(e)}")return None# 处理图片的函数
def process_image(img, block_size, C, method="gaussian", output_mode="white"):# 确保block_size为奇数block_size = int(block_size)if block_size % 2 == 0:block_size += 1# 如果图像不是灰度图,转换为灰度if len(img.shape) == 3:gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)else:gray_img = img# 选择自适应阈值方法adaptive_method = cv2.ADAPTIVE_THRESH_GAUSSIAN_Cif method == "average":adaptive_method = cv2.ADAPTIVE_THRESH_MEAN_C# 选择输出模式thresh_type = cv2.THRESH_BINARY_INVif output_mode == "black":thresh_type = cv2.THRESH_BINARY# 应用自适应阈值bin_img = cv2.adaptiveThreshold(gray_img, 255,adaptive_method,thresh_type,block_size,int(C))return bin_img# 将OpenCV图像转换为PySimpleGUI可显示的格式
def cv2_to_sg_image(cv_img, max_size=(800, 600)):"""转换图像并限制最大尺寸"""# 确保图像是RGB格式if len(cv_img.shape) == 2:display_img = cv2.cvtColor(cv_img, cv2.COLOR_GRAY2RGB)else:display_img = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)# 限制图像最大尺寸h, w = display_img.shape[:2]max_width, max_height = max_sizescale = min(max_width / w, max_height / h, 1.0)if scale < 1:new_w = int(w * scale)new_h = int(h * scale)display_img = cv2.resize(display_img, (new_w, new_h))# 转换为PIL图像,然后转换为字节img_pil = Image.fromarray(display_img)with io.BytesIO() as bio:img_pil.save(bio, format='PNG')return bio.getvalue()def main():# 创建GUIwindow = create_gui()# 初始化参数block_size = 9C = 3method = "gaussian"output_mode = "white"current_image_path = Noneoriginal_img = Noneprocessed_img = None# 初始显示默认图像 (确保原始图像在左,结果在右)default_img = np.full((300, 400, 3), 230, dtype=np.uint8)cv2.putText(default_img, "请选择图片", (120, 150),cv2.FONT_HERSHEY_SIMPLEX, 1, (100, 100, 100), 2)default_bytes = cv2_to_sg_image(default_img)window['-ORIGINAL-'].update(data=default_bytes)window['-RESULT-'].update(data=default_bytes)# 事件循环while True:event, values = window.read(timeout=100)  # 设置超时以便更新UI# 退出程序if event in (sg.WIN_CLOSED, '-EXIT-'):break# 重置参数if event == '-RESET-':block_size = 9C = 3method = "gaussian"output_mode = "white"window['-BLOCK_SIZE-'].update(block_size)window['-C-'].update(C)window['-BLOCK_SIZE_TXT-'].update(f'{block_size} (奇数)')window['-C_TXT-'].update(f'{C}')window['-METHOD-'].update('高斯加权')window['-OUTPUT-MODE-'].update('白线黑底')# 如果有当前图片,重新应用参数if current_image_path and original_img is not None:processed_img = process_image(original_img.copy(), block_size, C, method, output_mode)result_bytes = cv2_to_sg_image(processed_img)window['-RESULT-'].update(data=result_bytes)# 图片路径已更新if event == '-FILEPATH-' and values['-FILEPATH-']:img_path = values['-FILEPATH-']if os.path.exists(img_path):try:current_image_path = img_path# 使用支持中文路径的方式读取图片original_img = imread_chinese_path(img_path)if original_img is None:sg.popup_error("无法读取图片")continue# 显示原始图像 (左侧)original_bytes = cv2_to_sg_image(original_img)window['-ORIGINAL-'].update(data=original_bytes)# 应用当前参数处理图片block_size = values['-BLOCK_SIZE-']C = values['-C-']method = "gaussian" if values['-METHOD-'] == '高斯加权' else "average"output_mode = "white" if values['-OUTPUT-MODE-'] == '白线黑底' else "black"processed_img = process_image(original_img.copy(), block_size, C, method, output_mode)# 显示处理后的图像 (右侧)result_bytes = cv2_to_sg_image(processed_img)window['-RESULT-'].update(data=result_bytes)# 更新参数显示window['-BLOCK_SIZE_TXT-'].update(f'{int(block_size)} (奇数)')window['-C_TXT-'].update(f'{int(C)}')except Exception as e:sg.popup_error(f"错误: {str(e)}\n{traceback.format_exc()}")# 应用新参数if event == '-APPLY-' and current_image_path and original_img is not None:try:block_size = values['-BLOCK_SIZE-']C = values['-C-']method = "gaussian" if values['-METHOD-'] == '高斯加权' else "average"output_mode = "white" if values['-OUTPUT-MODE-'] == '白线黑底' else "black"window['-BLOCK_SIZE_TXT-'].update(f'{int(block_size)} (奇数)')window['-C_TXT-'].update(f'{int(C)}')# 处理图像processed_img = process_image(original_img.copy(), block_size, C, method, output_mode)# 显示处理后的图像 (右侧)result_bytes = cv2_to_sg_image(processed_img)window['-RESULT-'].update(data=result_bytes)except Exception as e:sg.popup_error(f"处理错误: {str(e)}")# 保存结果if event == '-SAVE-' and processed_img is not None:try:save_path = sg.popup_get_file('保存处理结果', save_as=True,file_types=(("PNG 文件", "*.png"),))if save_path:if not save_path.lower().endswith('.png'):save_path += '.png'# 保存处理后的图片(支持中文路径)success, buffer = cv2.imencode(".png", processed_img)if success:with open(save_path, "wb") as f:f.write(buffer.tobytes())sg.popup("保存成功", f"图片已保存至:\n{save_path}")else:sg.popup_error("保存失败:无法编码图像")except Exception as e:sg.popup_error(f"保存失败: {str(e)}")window.close()if __name__ == '__main__':main()

tkinter 免费的窗口组件

在这里插入图片描述

import cv2
import numpy as np
from PIL import Image, ImageTk
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import os
import io
import traceback# 支持中文路径的图像读取函数
def imread_chinese_path(filepath):"""使用cv2.imdecode读取中文路径图片"""try:with open(filepath, "rb") as stream:bytes_data = bytearray(stream.read())numpyarray = np.asarray(bytes_data, dtype=np.uint8)img = cv2.imdecode(numpyarray, cv2.IMREAD_UNCHANGED)return imgexcept Exception as e:messagebox.showerror("错误", f"读取图片错误: {str(e)}")return None# 处理图片的函数
def process_image(img, block_size, C, method="gaussian", output_mode="white"):# 确保block_size为奇数block_size = int(block_size)if block_size % 2 == 0:block_size += 1# 如果图像不是灰度图,转换为灰度if len(img.shape) == 3:gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)else:gray_img = img# 选择自适应阈值方法adaptive_method = cv2.ADAPTIVE_THRESH_GAUSSIAN_Cif method == "average":adaptive_method = cv2.ADAPTIVE_THRESH_MEAN_C# 选择输出模式thresh_type = cv2.THRESH_BINARY_INVif output_mode == "black":thresh_type = cv2.THRESH_BINARY# 应用自适应阈值bin_img = cv2.adaptiveThreshold(gray_img, 255,adaptive_method,thresh_type,block_size,int(C))return bin_imgclass AdaptiveThresholdApp:def __init__(self, root):self.root = rootself.root.title("自适应阈值二值化参数调节工具")self.root.geometry("900x800")self.root.resizable(True, True)# 初始化参数self.block_size = tk.IntVar(value=9)self.C = tk.IntVar(value=3)self.method = tk.StringVar(value="gaussian")self.output_mode = tk.StringVar(value="white")self.current_image_path = Noneself.original_img = Noneself.processed_img = None# 创建界面self.create_widgets()# 初始显示默认图像self.show_default_image()def create_widgets(self):# 顶部控制栏control_frame = ttk.Frame(self.root, padding=10)control_frame.pack(fill=tk.X)ttk.Button(control_frame, text="选择图片", command=self.select_image).pack(side=tk.LEFT, padx=5)ttk.Button(control_frame, text="重置参数", command=self.reset_parameters).pack(side=tk.LEFT, padx=5)ttk.Button(control_frame, text="保存结果", command=self.save_result).pack(side=tk.LEFT, padx=5)ttk.Button(control_frame, text="退出", command=self.root.quit).pack(side=tk.RIGHT, padx=5)# 图像显示区域image_frame = ttk.Frame(self.root, padding=10)image_frame.pack(fill=tk.BOTH, expand=True)# 原始图像面板original_frame = ttk.LabelFrame(image_frame, text="原始图像", padding=10)original_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5)self.original_label = ttk.Label(original_frame)self.original_label.pack(fill=tk.BOTH, expand=True)# 处理结果面板result_frame = ttk.LabelFrame(image_frame, text="二值化结果", padding=10)result_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=5)self.result_label = ttk.Label(result_frame)self.result_label.pack(fill=tk.BOTH, expand=True)# 参数控制区域param_frame = ttk.LabelFrame(self.root, text="参数设置", padding=10)param_frame.pack(fill=tk.X, padx=10, pady=10)# 邻域大小控制block_frame = ttk.Frame(param_frame)block_frame.pack(fill=tk.X, pady=5)ttk.Label(block_frame, text="邻域大小 (block_size):").pack(side=tk.LEFT)block_slider = ttk.Scale(block_frame, from_=3, to=41, orient=tk.HORIZONTAL,length=300, variable=self.block_size,command=self.update_block_size)block_slider.pack(side=tk.LEFT, padx=10)self.block_size_label = ttk.Label(block_frame, text="9 (奇数)", width=10)self.block_size_label.pack(side=tk.LEFT)# 调整常数控制c_frame = ttk.Frame(param_frame)c_frame.pack(fill=tk.X, pady=5)ttk.Label(c_frame, text="调整常数 (C):").pack(side=tk.LEFT)c_slider = ttk.Scale(c_frame, from_=-20, to=20, orient=tk.HORIZONTAL,length=300, variable=self.C,command=self.update_c)c_slider.pack(side=tk.LEFT, padx=10)self.c_label = ttk.Label(c_frame, text="3", width=10)self.c_label.pack(side=tk.LEFT)# 模式选择mode_frame = ttk.Frame(param_frame)mode_frame.pack(fill=tk.X, pady=5)ttk.Label(mode_frame, text="阈值模式:").pack(side=tk.LEFT)method_combo = ttk.Combobox(mode_frame, values=["高斯加权", "平均加权"],state="readonly", width=10)method_combo.set("高斯加权")method_combo.pack(side=tk.LEFT, padx=5)method_combo.bind("<<ComboboxSelected>>", self.update_method)ttk.Label(mode_frame, text="输出模式:").pack(side=tk.LEFT)output_combo = ttk.Combobox(mode_frame, values=["白线黑底", "黑线白底"],state="readonly", width=10)output_combo.set("白线黑底")output_combo.pack(side=tk.LEFT, padx=5)output_combo.bind("<<ComboboxSelected>>", self.update_output_mode)ttk.Button(mode_frame, text="应用参数", command=self.apply_parameters).pack(side=tk.RIGHT)# 帮助信息help_frame = ttk.LabelFrame(self.root, text="参数指南", padding=10)help_frame.pack(fill=tk.BOTH, padx=10, pady=10, expand=True)help_text = """
1. 邻域大小(block_size): 必须是奇数,影响细节保留能力- 值越大,抗噪能力越强,但可能模糊细节- 值越小,细节保留越好,但容易引入噪声2. 调整常数(C): 控制阈值的敏感度- 正值:降低阈值,保留更多细节- 负值:提高阈值,抑制噪点3. 阈值模式选择:- 高斯加权:考虑像素位置权重(推荐)- 平均加权:快速计算平均阈值推荐参数:
- 清晰图像: block_size=3-9, C=1-5
- 有噪图像: block_size=11-21, C=-3到0
"""help_label = ttk.Label(help_frame, text=help_text, justify=tk.LEFT)help_label.pack(fill=tk.BOTH, expand=True)def show_default_image(self):"""显示默认图像"""default_img = np.full((300, 400, 3), 230, dtype=np.uint8)cv2.putText(default_img, "请选择图片", (120, 150),cv2.FONT_HERSHEY_SIMPLEX, 1, (100, 100, 100), 2)# 转换为PIL图像pil_img = Image.fromarray(cv2.cvtColor(default_img, cv2.COLOR_BGR2RGB))pil_img = pil_img.resize((400, 300), Image.LANCZOS)# 显示在界面上self.original_photo = ImageTk.PhotoImage(pil_img)self.result_photo = ImageTk.PhotoImage(pil_img)self.original_label.config(image=self.original_photo)self.result_label.config(image=self.result_photo)def update_block_size(self, value):"""更新邻域大小显示"""block_size = int(float(value))if block_size % 2 == 0:block_size += 1self.block_size_label.config(text=f"{block_size} (奇数)")def update_c(self, value):"""更新调整常数显示"""c_value = int(float(value))self.c_label.config(text=str(c_value))def update_method(self, event):"""更新阈值模式"""method = event.widget.get()self.method.set("gaussian" if method == "高斯加权" else "average")def update_output_mode(self, event):"""更新输出模式"""output_mode = event.widget.get()self.output_mode.set("white" if output_mode == "白线黑底" else "black")def select_image(self):"""选择图片"""file_path = filedialog.askopenfilename(filetypes=[("图片文件", "*.png;*.jpg;*.jpeg;*.bmp")])if not file_path:returnself.current_image_path = file_pathtry:# 读取图片self.original_img = imread_chinese_path(file_path)if self.original_img is None:messagebox.showerror("错误", "无法读取图片")return# 显示原始图像self.show_original_image()# 处理图片self.process_image()except Exception as e:messagebox.showerror("错误", f"处理图片时出错:\n{str(e)}\n{traceback.format_exc()}")def show_original_image(self):"""显示原始图像"""# 转换为RGB格式if len(self.original_img.shape) == 2:display_img = cv2.cvtColor(self.original_img, cv2.COLOR_GRAY2RGB)else:display_img = cv2.cvtColor(self.original_img, cv2.COLOR_BGR2RGB)# 限制最大尺寸h, w = display_img.shape[:2]max_size = 400scale = min(max_size / w, max_size / h, 1.0)if scale < 1:new_w = int(w * scale)new_h = int(h * scale)display_img = cv2.resize(display_img, (new_w, new_h))# 转换为PIL图像pil_img = Image.fromarray(display_img)# 显示在界面上self.original_photo = ImageTk.PhotoImage(pil_img)self.original_label.config(image=self.original_photo)def process_image(self):"""处理图片并显示结果"""if self.original_img is None:returntry:# 处理图片self.processed_img = process_image(self.original_img.copy(),self.block_size.get(),self.C.get(),self.method.get(),self.output_mode.get())# 显示处理结果self.show_processed_image()except Exception as e:messagebox.showerror("错误", f"处理图片时出错:\n{str(e)}\n{traceback.format_exc()}")def show_processed_image(self):"""显示处理后的图像"""if self.processed_img is None:return# 转换为RGB格式display_img = cv2.cvtColor(self.processed_img, cv2.COLOR_GRAY2RGB)# 限制最大尺寸h, w = display_img.shape[:2]max_size = 400scale = min(max_size / w, max_size / h, 1.0)if scale < 1:new_w = int(w * scale)new_h = int(h * scale)display_img = cv2.resize(display_img, (new_w, new_h))# 转换为PIL图像pil_img = Image.fromarray(display_img)# 显示在界面上self.result_photo = ImageTk.PhotoImage(pil_img)self.result_label.config(image=self.result_photo)def apply_parameters(self):"""应用参数处理图片"""self.process_image()def reset_parameters(self):"""重置参数"""self.block_size.set(9)self.C.set(3)self.method.set("gaussian")self.output_mode.set("white")self.block_size_label.config(text="9 (奇数)")self.c_label.config(text="3")# 如果有当前图片,重新应用参数if self.current_image_path and self.original_img is not None:self.process_image()def save_result(self):"""保存处理结果"""if self.processed_img is None:messagebox.showinfo("提示", "没有可保存的处理结果")returnfile_path = filedialog.asksaveasfilename(defaultextension=".png",filetypes=[("PNG 文件", "*.png")])if not file_path:returntry:# 保存处理后的图片(支持中文路径)success, buffer = cv2.imencode(".png", self.processed_img)if success:with open(file_path, "wb") as f:f.write(buffer.tobytes())messagebox.showinfo("保存成功", f"图片已保存至:\n{file_path}")else:messagebox.showerror("保存失败", "无法编码图像")except Exception as e:messagebox.showerror("保存失败", f"保存图片时出错:\n{str(e)}")if __name__ == "__main__":root = tk.Tk()app = AdaptiveThresholdApp(root)root.mainloop()
http://www.lryc.cn/news/623024.html

相关文章:

  • vscode中用python调用matlab的函数(环境安装)
  • 计算机网络:(十五)TCP拥塞控制与拥塞控制算法深度剖析
  • 安全审计-firewall防火墙
  • 在STM32F103上进行FreeRTOS移植和配置(STM32CubeIDE)
  • MySQL的《Buffer-pool》和《连接池》介绍
  • LangChain4j:基于 SSE 与 Flux 的 AI 流式对话实现方案
  • lesson40:PyMySQL完全指南:从基础到高级的Python MySQL交互
  • 数据结构:层序遍历 (Level-order Traversal)
  • 图论Day4学习心得
  • Kafka 面试题及详细答案100道(11-22)-- 核心机制1
  • 代码随想录Day52:图论(孤岛的总面积、沉没孤岛、水流问题、建造最大岛屿)
  • Cmake学习笔记
  • 代码随想录算法训练营四十三天|图论part01
  • 数字化与人工智能的崛起及其社会影响研究报告
  • 基于uni-app+vue3实现的微信小程序地图范围限制与单点标记功能实现指南
  • Altium Designer 22使用笔记(7)---网表导入,叠层设置
  • 【电路笔记 通信】AXI4-Lite协议 论文阅读 简化的高级可扩展接口(AdvancedeXtensibleInterface4Lite)
  • 【计算机网络架构】混合型架构简介
  • 车载诊断架构 --- 怎么解决对已量产ECU增加具体DTC的快照信息?
  • 超越Transformer:大模型架构创新的深度探索
  • 【自动化运维神器Ansible】Ansible逻辑运算符详解:构建复杂条件判断的核心工具
  • 11、软件需求工程
  • 【系统分析师】软件需求工程——第11章学习笔记(下)
  • 架构调整决策
  • 软件需求管理过程详解
  • M-LAG双活网关
  • linux I2C核心、总线与设备驱动
  • 特洛伊木马和后门程序的定义、联系、区别与应用场景
  • UE5多人MOBA+GAS 45、制作冲刺技能
  • 深入详解PCB布局布线技巧-去耦电容的摆放位置