
简介本资源是一套面向计算机视觉初学者与图像处理课程实践者的C语言实现方案聚焦多聚焦图像融合这一典型图像增强任务解决多源图像因景深限制导致局部模糊、细节丢失的问题适用于医学影像分析、遥感图像合成及智能机器人视觉等场景。压缩包共9个文件含8幅BMP格式的配对测试图像如1_a.bmp/1_b.bmp等覆盖不同聚焦区域的原始输入核心为1个完整可编译的C源文件zy.c封装了清晰度计算基于梯度绝对值、图像分块、块级一致性检验及最大值选择融合策略等关键逻辑代码结构清晰、注释充分便于理解算法原理与工程落地细节。资源大小仅1.05MB轻量易部署已有212人学习下载适合课程大作业实践、算法复现验证及嵌入式平台移植参考。1. 项目概述当清晰的视野需要“拼图”在计算机视觉和图像处理领域我们常常会遇到一个看似简单却颇为棘手的问题如何得到一张所有区域都清晰的照片想象一下你拿着相机拍摄一个纵深场景比如书桌上近处的键盘和远处的书架。由于镜头景深的物理限制你很难在一次拍摄中让前景和背景同时清晰对焦。拍一张键盘清晰了书架模糊再拍一张书架清晰了键盘又成了虚影。这种因对焦点不同而产生的一系列图像就是“多聚焦图像”。“基于清晰度的多聚焦图像融合算法”要解决的正是这个“鱼与熊掌不可兼得”的难题。它的核心思想非常直观从同一场景下拍摄的多张焦点不同的图像中像一位经验丰富的拼图师挑选出每一张里最清晰的部分然后将它们天衣无缝地“缝合”在一起最终生成一张全局清晰的合成图像。这个过程我们称之为“图像融合”。为什么选择C语言来实现这背后有非常实际的考量。图像处理尤其是像素级的融合操作本质上是海量数据的密集计算。一张普通的1080p图像就有超过200万个像素点每个像素点又包含RGB三个通道。算法需要在毫秒级的时间内对数以百万计的数据点进行反复比较、判断和赋值。C语言以其接近硬件的特性、卓越的执行效率和对内存的精细控制能力成为实现这类计算密集型算法的绝佳选择。它没有高级语言那些花哨的抽象和自动垃圾回收带来的不确定性程序员可以直接操纵内存块用指针高效地遍历图像数据将每一个CPU周期都用在刀刃上。对于追求实时性如显微成像、工业检测或需要在资源受限的嵌入式设备如某些监控摄像头、无人机上运行的应用场景用C语言从底层实现融合算法是确保性能与可靠性的不二法门。2. 核心思路与算法设计拆解一个完整的基于清晰度的多聚焦图像融合流程可以清晰地划分为三个核心阶段清晰度评价、决策图生成与优化、以及最终的像素融合。整个算法的设计都围绕着如何准确、鲁棒地完成这三个任务展开。2.1 清晰度评价如何定义“清晰”算法的第一步也是最为关键的一步就是为图像中的每一个局部区域打分量化其“清晰度”。清晰度本质上反映了图像细节的丰富程度和边缘的锐利程度。一个清晰的区域其像素灰度值的变化往往更剧烈、更复杂。2.1.1 常见清晰度评价算子在C语言实现中我们通常会在空间域即直接对像素灰度值操作计算清晰度。以下是几种经典且计算高效的算子梯度能量Energy of Gradient, EOG这是最直观的方法之一。清晰图像的边缘处灰度值变化大梯度值一阶导数也大。我们计算每个像素点在水平Gx和垂直Gy方向上的梯度通常用Sobel、Prewitt等算子卷积得到然后将梯度幅值的平方和作为该点的清晰度度量。对于一个像素点(x, y)其梯度能量F(x, y) Gx(x, y)^2 Gy(x, y)^2。最后通常取一个以该点为中心的N×N小窗口如3x3, 5x5内所有像素梯度能量的和或平均值作为该窗口中心点的清晰度得分。这种方法对噪声比较敏感。拉普拉斯能量Energy of Laplacian, EOL拉普拉斯算子是二阶导数算子它对图像中的灰度突变如边缘响应更强烈。计算图像经过拉普拉斯卷积后的结果L(x, y)然后同样计算窗口内的能量和F(x, y) Σ (L(i, j)^2)其中(i, j)在窗口内。EOL比EOG对焦点更敏感但同样受噪声影响。空间频率Spatial Frequency, SF它从频率角度衡量清晰度。分别计算行频率RF和列频率CFRF sqrt( (1/(M*N)) * Σ Σ [I(x, y) - I(x, y-1)]^2 )CF sqrt( (1/(M*N)) * Σ Σ [I(x, y) - I(x-1, y)]^2 )其中M, N是图像尺寸求和遍历所有像素。总的空间频率SF sqrt(RF^2 CF^2)。SF值越大表示图像空间变化越快即越清晰。计算时需要遍历全图通常用于评价整图或较大分块。2.1.2 算子选择与窗口大小的权衡在C语言实现时选择哪个算子需要进行权衡。梯度能量EOG计算相对简单速度最快适合对实时性要求极高的场景。拉普拉斯能量EOL对焦点的判别能力通常更强能产生对比更鲜明的清晰度图但计算量稍大。对于噪声较大的图像可以在卷积前先进行一个简单的高斯模糊预处理或者考虑使用更复杂的评价方法如基于小波变换的但这会显著增加计算复杂度。另一个关键参数是评价窗口的大小。窗口太小如1x1即单像素评价结果会非常不稳定极易受噪声干扰。窗口太大则会模糊不同清晰区域之间的边界导致决策图不精确在融合结果中产生“重影”或模糊的过渡带。经过大量实践对于大多数自然图像5x5或7x7的窗口是一个较好的起点。它能在噪声鲁棒性和边界定位精度之间取得不错的平衡。实操心得在C语言中实现这些卷积运算时切忌使用多层嵌套循环对每个像素进行完整的卷积核计算那将极其低效。正确的做法是利用可分离卷积核的特性如Sobel、高斯先进行行卷积再进行列卷积或者直接使用预先计算好的N×N卷积模板通过指针偏移来高效访问邻域像素。例如计算5x5窗口的梯度能量和可以预先分配好行缓冲区用滑动窗口的方式更新避免重复计算。2.2 决策图生成做出“谁更清晰”的选择得到每张输入图像对应的清晰度图一个和原图等大的矩阵每个像素值代表该点所在区域的清晰度得分后下一步就是生成“决策图”。决策图是一个标签图其每个像素位置的值指示了最终融合结果中该像素应该来自哪一张输入图像。2.2.1 初始决策赢家通吃最朴素的方法是“赢家通吃”Winner-Take-All。对于融合位置(x, y)我们比较所有输入图像在该位置的清晰度得分将得分最高的那个图像的索引赋给决策图D(x, y)。// 假设有2张图clarity_map1和clarity_map2是它们的清晰度图 for (int y 0; y height; y) { for (int x 0; x width; x) { if (clarity_map1[y][x] clarity_map2[y][x]) { decision_map[y][x] 0; // 选择图1 } else { decision_map[y][x] 1; // 选择图2 } } }这种方法简单直接但会产生一个严重问题决策图会充满孤立的、椒盐噪声一样的错误像素点。因为清晰度评价本身存在局部波动可能在物体边缘附近A图比B图清晰度高一点点但周围像素都是B图更清晰这就产生了一个孤立的A图决策点。2.2.2 决策图优化平滑与一致性原始的“赢家通吃”决策图是不可用的必须进行优化。优化的目标是使决策区域连通、平滑并且与图像的语义边缘尽可能对齐。常用方法包括形态学滤波这是最常用且高效的后处理手段。先使用闭运算先膨胀后腐蚀来填充决策图中小的空洞和狭窄的缝隙再使用开运算先腐蚀后膨胀来消除小的孤立噪声点。结构元素如3x3或5x5的圆形或方形的大小决定了平滑的力度。小区域去除连通域分析。遍历决策图找出所有连通的决策区域例如所有标记为“图1”的像素组成的区域计算每个区域的面积。将面积小于某个阈值如总像素数的0.1%的区域根据其周围像素的主流决策进行重新赋值。这可以有效地清除那些顽固的椒盐噪声。引导滤波这是一种更高级的优化方法。以其中一张输入图像或它们的平均作为引导图对初始决策图需要转换为概率形式或连续值进行滤波。引导滤波能在平滑决策图的同时保持其边缘与引导图的边缘一致效果通常优于形态学滤波但计算量更大。在C语言实现中形态学滤波结合小区域去除是性价比最高的方案。你需要自己实现膨胀、腐蚀、连通域标记等算法。虽然有些繁琐但一旦实现其运行速度极快且效果对于大多数应用已足够好。2.3 像素级融合从决策到合成有了优化后的决策图最后一步就是合成最终图像。这看似简单却隐藏着影响视觉效果的关键细节。2.3.1 直接复制与边界效应最简单的方法是直接根据决策图从对应的输入图像中拷贝像素值for (int y 0; y height; y) { for (int x 0; x width; x) { int src_idx decision_map[y][x]; fused_img[y][x] input_imgs[src_idx][y][x]; } }但这样做会在不同决策区域的边界处产生生硬的“接缝”因为两张输入图像在边界处的像素值可能因为对焦不同而有显著差异直接拼接会导致明显的跳跃。2.3.2 过渡带平滑羽化为了解决边界生硬的问题必须在决策边界附近建立一个平滑的过渡带。常用方法是距离变换加权平均。对二值化的决策图例如0代表图11代表图2进行距离变换。对于决策为“图1”的每个像素计算它到最近的“图2”决策像素的欧氏距离得到距离图dist_to_region2。同理得到dist_to_region1。在决策边界两侧一定宽度如5-15个像素的区域内计算权重。对于区域内的一个像素其来自图1的权重w1 dist_to_region2 / (dist_to_region1 dist_to_region2)来自图2的权重w2 1 - w1。这样在边界上距离相等权重各为0.5越深入图1区域w1越接近1越深入图2区域w1越接近0。在过渡带内融合像素值fused_pixel w1 * pixel_img1 w2 * pixel_img2。这种方法能有效消除接缝产生自然的过渡。在C语言中实现距离变换有专门的算法如倒角距离对于实时性要求不高的场合也可以使用一种较慢但简单的两遍扫描算法。注意事项过渡带的宽度需要谨慎选择。太窄平滑效果不足太宽可能会将原本清晰区域的部分像素与模糊区域混合导致融合结果局部模糊。通常需要根据图像分辨率和内容复杂程度进行实验调整。3. C语言实现详解与核心代码剖析理论清晰后我们进入实战环节。用C语言实现这个算法是对编程能力和图像处理理解的综合考验。我们将遵循模块化设计构建一个清晰、高效且易于调试的程序结构。3.1 数据结构与内存管理高效处理图像首先要设计好数据的容器。我们拒绝使用低效的二维数组动态声明如int arr[m][n]在变长时很麻烦而是采用一维数组模拟二维并封装成结构体。// 图像数据结构体 typedef struct { int width; int height; int channels; // 灰度图为1彩色RGB图为3 unsigned char *data; // 按行优先存储的一维数组 data[y*width*channels x*channels c] } Image; // 清晰度图/决策图浮点型或整型 typedef struct { int width; int height; float *data; // 或 int *data用于决策图 } Map;内存管理要点统一分配与释放为Image和Map编写专门的创建create_image和销毁free_image函数确保malloc和free配对避免内存泄漏。行指针数组为了提高访问效率可以在初始化时额外创建一个unsigned char **row_ptrs数组使得row_ptrs[y][x]能快速访问像素。但这会增加内存和管理复杂度对于性能瓶颈不在像素访问的项目直接用一维数组计算偏移足矣。字节对齐对于需要用到SIMD如SSE指令集加速的情况分配内存时要注意字节对齐使用aligned_alloc或posix_memalign。3.2 清晰度图计算模块实现我们以实现拉普拉斯能量EOL为例展示一个5x5窗口的清晰度计算函数。/** * 计算单通道灰度图像的拉普拉斯能量清晰度图 * param src 输入灰度图像 * param window_size 评价窗口大小奇数如5 * return 返回计算出的清晰度图Map结构 */ Map calculate_laplacian_energy_map(const Image* src, int window_size) { // 参数检查略... int w src-width; int h src-height; // 1. 首先计算图像的拉普拉斯响应 float* laplacian (float*)malloc(w * h * sizeof(float)); // 使用3x3拉普拉斯核[[0,1,0],[1,-4,1],[0,1,0]] for (int y 1; y h - 1; y) { for (int x 1; x w - 1; x) { unsigned char* p src-data y * w x; float lap (float)p[-w] // 上 p[-1] // 左 p[1] // 右 p[w] // 下 - 4.0f * p[0]; // 中 laplacian[y * w x] lap * lap; // 直接存平方 } } // 边界处理简单复制或置零 for (int x 0; x w; x) { laplacian[x] 0; laplacian[(h-1)*w x] 0; } for (int y 0; y h; y) { laplacian[y*w] 0; laplacian[y*w w-1] 0; } // 2. 滑动窗口求和得到清晰度图 Map clarity_map create_map(w, h); int radius window_size / 2; float window_norm 1.0f / (window_size * window_size); // 可选计算平均值 for (int y radius; y h - radius; y) { for (int x radius; x w - radius; x) { float sum 0.0f; // 遍历窗口内所有像素 for (int wy -radius; wy radius; wy) { const float* lap_row laplacian (y wy) * w; for (int wx -radius; wx radius; wx) { sum lap_row[x wx]; } } clarity_map.data[y * w x] sum; // 或 sum * window_norm } } // 清晰度图边界区域无法计算完整窗口用最近的有效值填充或置零 // ... 边界填充代码 ... free(laplacian); return clarity_map; }性能优化技巧积分图优化上述代码中内层循环对每个像素都要遍历window_size*window_size次复杂度是O(N * window^2)。对于大窗口或高分辨率图像这是性能瓶颈。积分图技术可以将任意矩形区域求和优化到O(1)。我们先对laplacian存储的是平方值计算积分图integral那么窗口中能量和sum integral(yr, xr) - integral(y-r-1, xr) - integral(yr, x-r-1) integral(y-r-1, x-r-1)。这能带来数十倍的性能提升尤其适合窗口较大的情况。多线程并行清晰度图的计算是完美的数据并行任务。可以使用OpenMP指令轻松地将最外层的行循环并行化#pragma omp parallel for。确保每个线程操作独立的内存区域。3.3 决策图生成与优化模块假设我们有两张输入图像img1和img2并已计算出对应的清晰度图clarity1和clarity2。/** * 生成并优化初始决策图二值0/1 */ Image* generate_and_optimize_decision_map(const Map* clarity1, const Map* clarity2, int width, int height) { // 1. 生成初始决策图 (0: 图1更清晰 1: 图2更清晰) Image* init_decision create_image(width, height, 1); // 创建单通道图像存储0/1 for (int i 0; i width * height; i) { init_decision-data[i] (clarity1-data[i] clarity2-data[i]) ? 0 : 1; } // 2. 形态学滤波优化 Image* temp create_image(width, height, 1); // 2.1 闭运算先膨胀后腐蚀填充小洞和细缝 morphological_operation(init_decision, temp, MORPH_DILATE, 3); // 使用3x3结构元素膨胀 morphological_operation(temp, init_decision, MORPH_ERODE, 3); // 腐蚀结果存回init_decision // 2.2 开运算先腐蚀后膨胀去除小噪声点 morphological_operation(init_decision, temp, MORPH_ERODE, 3); morphological_operation(temp, init_decision, MORPH_DILATE, 3); free_image(temp); // 3. 小区域去除 remove_small_regions(init_decision, 0.001); // 移除面积小于0.1%总像素的区域 return init_decision; }morphological_operation和remove_small_regions需要自行实现。形态学操作的核心是遍历图像对每个像素查看其邻域结构元素范围内根据操作类型膨胀取邻域最大值腐蚀取最小值来设置输出像素值。小区域去除通常使用经典的两遍扫描连通组件标记算法Union-Find第一遍扫描分配临时标签并记录等价关系第二遍解析等价关系并重写标签最后统计各标签面积并进行过滤。3.4 融合与过渡带平滑模块/** * 根据优化后的决策图进行带过渡带平滑的图像融合 * param img1, img2 输入图像假设已对齐 * param decision 优化后的二值决策图 (0/1) * param transition_width 过渡带宽度像素 * return 融合后的图像 */ Image* fuse_with_transition(const Image* img1, const Image* img2, const Image* decision, int transition_width) { // 假设是灰度图彩色图需对每个通道分别处理 Image* fused create_image(img1-width, img1-height, img1-channels); // 第一步计算决策边界处的距离图这里简化计算到最近决策边界的近似距离 // 更精确的做法是使用真正的距离变换算法 Map dist_to_boundary compute_distance_to_boundary(decision, transition_width); for (int y 0; y img1-height; y) { for (int x 0; x img1-width; x) { int idx y * img1-width x; float d dist_to_boundary.data[idx]; // d在[0, transition_width]间0表示在边界上 unsigned char pixel1 img1-data[idx]; unsigned char pixel2 img2-data[idx]; unsigned char dec decision-data[idx]; float weight1, weight2; if (d transition_width) { // 远离边界直接使用决策 weight1 (dec 0) ? 1.0f : 0.0f; weight2 1.0f - weight1; } else { // 在过渡带内平滑权重 // 假设决策为0的区域越靠近边界中心d小其权重越接近0.5 // 一种简单的线性权重weight1 0.5 (dec 0 ? 1 : -1) * (d / (2*transition_width)) // 更自然的是使用sigmoid函数或cosine函数 float t d / transition_width; // 归一化距离 float alpha (dec 0) ? (0.5f 0.5f * t) : (0.5f - 0.5f * t); weight1 alpha; weight2 1.0f - alpha; } fused-data[idx] (unsigned char)(weight1 * pixel1 weight2 * pixel2 0.5f); // 四舍五入 } } free_map(dist_to_boundary); return fused; }compute_distance_to_boundary函数的实现是过渡带平滑效果好坏的关键。一个简单但有效的近似方法是对决策图进行几次迭代腐蚀每次腐蚀掉的像素就是距离边界为i的像素i为迭代次数。记录下每个像素第一次被腐蚀掉的迭代次数这个次数就可以作为其到边界的近似距离。这种方法计算量小效果尚可。4. 实战调试、性能瓶颈与优化策略将各个模块组合成一个完整的程序后真正的挑战才刚刚开始调试和优化。4.1 调试与可视化技巧图像算法调试光看数字不行必须“看见”中间结果。清晰度图可视化清晰度图是浮点数矩阵需要归一化到0-255才能显示。注意清晰度值的动态范围可能很大直接线性归一化可能导致大部分区域显示为黑色。可以采用对比度拉伸或伽马校正来增强可视化效果。更专业的做法是计算其直方图然后进行直方图均衡化。// 将浮点清晰度图转换为8位灰度图用于显示 Image* visualize_clarity_map(const Map* map) { Image* vis create_image(map-width, map-height, 1); float min_val, max_val; find_min_max(map-data, map-width*map-height, min_val, max_val); float range max_val - min_val; if (range 1e-6) range 1.0f; for (int i 0; i map-width * map-height; i) { float normalized (map-data[i] - min_val) / range; // 可选伽马校正 gamma 0.5 增强暗部细节 // normalized powf(normalized, 0.5f); vis-data[i] (unsigned char)(normalized * 255.0f); } return vis; }决策图可视化将二值决策图0/1直接乘以255显示为黑白图像。优化前后的决策图对比能直观看出形态学滤波和小区域去除的效果。边界检查在融合阶段输出一个“边界图”其中过渡带内的像素用特定灰度值标记。这有助于你调整transition_width参数观察过渡是否平滑自然。4.2 性能瓶颈分析与优化用clock()或gettimeofday()函数给每个主要模块计时定位瓶颈。瓶颈一清晰度计算。如果未使用积分图这几乎是耗时的绝对大头。优化策略无条件使用积分图算法。自己实现一个高效的积分图计算函数注意使用循环展开和指针优化减少计算开销。瓶颈二形态学滤波。标准的腐蚀膨胀算法每个像素都要与邻域比较复杂度高。优化策略使用可分离结构元素如果使用矩形结构元素腐蚀和膨胀可以分解为先行后列的两步一维操作将复杂度从O(N * k^2)降到O(2N * k)其中k是窗口大小。使用滑动窗口最值算法对于一维的腐蚀/膨胀可以使用单调队列在O(N)时间内完成而不是O(N*k)。这对于较大结构元素效果显著。瓶颈三连通域分析小区域去除。两遍扫描法本身效率不错但在标记等价关系时Union-Find的find操作可能因树退化为链而变慢。优化策略实现带路径压缩和按秩合并的Union-Find这是保证算法接近线性时间复杂度的关键。内存访问模式C语言中缓存不友好是隐形的性能杀手。确保你的核心循环如遍历图像是按行连续访问内存的即内层循环遍历x。像data[y][x]这样的访问如果data是指针的指针且每行内存不连续会造成大量缓存失效。坚持使用一维数组加行偏移的计算方式。4.3 效果评估与参数调优算法实现后如何评价融合效果除了肉眼观察可以使用一些客观指标信息熵融合图像的熵值应高于任一源图像表明信息量增加。空间频率融合图像的空间频率应更高表明细节更丰富。边缘保持度可以计算融合图像与理想清晰图像如果存在之间的边缘关联性指标如Q^AB/F。互信息衡量融合图像从源图像中保留了多少信息。对于参数调优如清晰度评价窗口大小、形态学滤波核大小、过渡带宽度没有放之四海而皆准的“黄金值”。你需要准备一个具有代表性的测试图像集包含不同场景、不同模糊程度编写脚本批量运行不同参数组合并计算客观指标绘制曲线找到在多数场景下表现稳定的参数范围。通常窗口大小在5-11像素奇数形态学核尺寸在3-5像素过渡带宽度在3-7像素是一个不错的起始搜索区间。5. 常见问题排查与进阶思考在实际编码和运行中你肯定会遇到各种问题。这里记录一些典型坑位和解决方法。问题一融合结果在物体边缘出现“鬼影”或模糊。原因决策图不够精确边界定位不准导致过渡带覆盖了本应完全属于清晰区域的像素。排查可视化优化前后的决策图。如果决策图边界锯齿严重或存在大量孤立点说明清晰度评价可能受噪声影响太大或者形态学滤波的核大小不合适。解决尝试使用对噪声更不敏感的清晰度算子或在计算清晰度前对图像进行轻微的高斯平滑。调整形态学滤波的顺序和核大小。可以先做一次小核3x3的开运算去噪再做一次稍大核5x5的闭运算填充。考虑使用引导滤波来优化决策图它能更好地保持边缘。问题二算法对某些特定纹理如重复图案、纯色区域的清晰度判断失灵。原因梯度或拉普拉斯算子在纹理单一、低频的区域响应很弱导致清晰度得分没有区分度。解决引入多尺度清晰度评价。在不同尺度如图像金字塔的不同层上计算清晰度然后进行融合。在粗尺度上大结构如整个物体的清晰度对比更明显在细尺度上纹理细节的清晰度对比更明显。将多尺度的清晰度图通过某种规则如取最大值合并能得到更鲁棒的清晰度估计。问题三处理彩色图像时效果不佳或颜色异常。原因直接对RGB三个通道分别做融合然后在边界处三个通道的决策可能不一致导致颜色失真。解决标准做法是转换到YUV或Lab颜色空间。只在亮度通道Y或L上进行清晰度评价、决策图生成和融合操作。色度通道UV或ab则采用简单平均、或根据亮度通道的权重进行加权平均。因为人眼对亮度细节的敏感度远高于颜色细节这样做既能保持清晰的边缘又能避免颜色突变。问题四输入图像未严格对齐导致融合结果重影。前提多聚焦融合算法强烈要求输入图像在空间上完全对齐只有对焦点不同。如果图像因手持拍摄有轻微平移或旋转必须先进行图像配准。方案在融合流程前端加入配准模块。可以使用特征点匹配如SIFT、ORB但对于模糊图像可能失效或基于相位的配准方法。对于纯平移计算两幅图像的互相关并找到峰值是最简单的方法。这是一个独立的复杂课题如果输入图像无法保证对齐就必须优先解决它。从“能用”到“好用”这个基于清晰度的多聚焦图像融合项目就像在C语言的精密机床上一颗颗打磨零件最终组装成一台功能强大的仪器。每一个循环的优化每一个指针的精准操作都直接关系到最终那幅“全清晰”图像的生成速度与质量。这个过程里最大的收获或许不是最终的程序而是那种对数据流动、对计算效率、对问题本质的深刻理解。当你看到自己写的C程序将两张模糊各异的照片干净利落地合成一张处处清晰的画面时那种满足感是调用现成库函数无法比拟的。它提醒我们在追求高效与简洁的现代编程世界之外还存在一个由底层控制力和算法直觉构筑的、充满挑战与乐趣的领域。本文还有配套的精品资源点击获取