资讯详情

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

📅 2026/10/18 0:02:15 | 华诺云谱 👁 阅读
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践
数字图像处理这四个字听起来挺唬人但落到具体操作上无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程对图像做反白、改调色板、彩色转灰度最后把像素数据导出成txt文件。单看每一步都很简单但合在一起跑一遍你会彻底想明白一件平时容易忽略的事——图像文件在内存里到底长什么样像素值是怎么被存储、计算和读出来的。这篇内容就把这套流程从头到尾拆开包括原理、代码、输出结果还有我踩过的几个坑。适合正在学数字图像处理的人也适合做课设或毕设时想把基础环节快速跑通的同学。1. 项目思路拆解为什么这四步要凑在一起做1.1 四个操作背后其实是同一条逻辑线很多人第一次接触这些操作时会觉得它们是零散的反白是一个滤镜转灰度是一个功能改调色板是一个偏门技巧存txt更像是在应付作业。但把它们放在一起你会发现它们全都在处理同一件事像素值。反白是直接修改图像矩阵里的数值改调色板不碰像素值改的是“如何解释这些数值”彩色转灰度是把三维的像素矩阵降成二维导出txt则是把最终矩阵变成人能直接读懂的纯文本。前面三个都是在改变或操作数据最后一个是把数据“打印”出来。你亲手做完这四个步骤相当于把一张图从“文件”到“矩阵”再到“可读文本”的整条链路走了一遍这个认知比任何单独的操作都有价值。我在设计这套流程的时候特意让每一步都保留中间结果而不是只输出最终效果。比如反白完保存一张灰度化完再保存一张调色板改完再保存一张每一步都能单独打开看。这样做的好处是出了问题能立刻定位是哪一步不对而不是最后拿着一个莫名其妙的结果无从下手。A同学跟我一起跑这套流程的时候一开始就是偷懒只留最终文件结果颜色不对根本不知道是灰度化时通道顺序错了还是调色板那儿出了问题后来老老实实每步留底几分钟就找到问题了。1.2 工具选型Python OpenCV Pillow NumPy这套流程用到的工具我首选的是Python搭配三件套OpenCV、PillowPIL、NumPy。为什么这么选因为它们在图像处理上的分工刚好互补。OpenCV在颜色空间转换、像素运算上非常成熟一个cvtColor就能完成灰度化bitwise_not可以直接做反白操作性能也很好。但它对调色板这种索引色图像的支持比较别扭很多接口不会直接暴露调色板给你改。这时候Pillow就是更好的选择它原生支持P模式索引色模式可以直接getpalette()拿到调色板列表改完再putpalette()写回去非常直观。NumPy则是这一切的地基图像在内存里本质就是一个多维数组反白操作对一个数组做减法你不可能用双层for循环去逐像素处理那样又慢又容易错直接用NumPy的向量化运算一行代码就能处理整张图。这三个工具装起来也简单一个pip install opencv-python pillow numpy就全搞定了不涉及额外环境。如果你用的是虚拟环境记得装完之后确认一下import没问题OpenCV在部分精简环境里缺少依赖库会报奇怪的错误最常见的就是缺了基础的动态链接库。我在某高校的机器上就碰到过这种问题当时还以为是代码错了排查了半天最后发现是环境问题。2. 图像反白实操从“底片效果”到像素取反2.1 反白的数学本质与应用场景反白其实就是图像处理里的灰度反转教科书上叫“反色变换”也有人叫“负片效果”。它的数学定义很简单对图像中每个像素点的亮度值V用最大亮度值减去它。对于最常见的8位灰度图像素值范围是0到255反白就是new_value 255 - old_value。黑色0反白后变成白色255白色变成黑色中间灰度值变成它的互补值128反白后还是128。所以反白是一个典型的点操作也叫逐点运算每个像素的新值只跟它自己的旧值有关完全不依赖周围的像素。这意味着你不需要看周围的像素来做判断可以用向量化方式对整张图同时计算速度极快。你可以用“照片底片”来理解老式胶片上亮的地方曝光多底片上是黑的洗出来才是亮的数字负片本质上就是这个反转过程的功能模拟。反白不只是一个视觉效果它有实际用途。医学影像里看X光片时经常用灰度反转来突出某些组织印刷行业做负片预览会用到它图像增强里面对某些太暗的区域先反白成亮的区域再处理往往比直接拉伸暗部更方便。我自己的习惯是拿到一张偏暗的图像第一件事就是先反白看一眼很多时候暗部的细节在反白之后反而看得更清楚这一步成本极低却能帮你快速判断图像数据的分布情况。2.2 反白的两种实现与注意事项第一种实现用OpenCV一句话的事import cv2 img cv2.imread(input.png) inverted cv2.bitwise_not(img) # 逐位取反等效于255 - img cv2.imwrite(inverted.png, inverted)第二种用NumPy更直白地展示了数学本质import cv2 import numpy as np img cv2.imread(input.png) inverted 255 - img cv2.imwrite(inverted.png, inverted)两种做法效果完全一样bitwise_not本质上就是按位取反对8位无符号整数来说按位取反的结果恰好等于255减去原值。我通常用第二种写法因为它在代码里直接体现了公式别人看你的代码更容易理解你在干什么。这里有个非常容易翻车的点图像的数据类型。如果图像是以uint80到255读入的那255 - img没问题。但如果图像被读成浮点类型比如数值范围在0.0到1.0之间那正确的反白应该是1.0 - img。你要是还写255 - img得到的就是一片乱七八糟的数值保存出来全白或者全黑。同理处理16位深图像时要用65535 - img。判断图像类型很简单print(img.dtype)一看就知道。还有一个细节是反白只对单通道或三通道图有意义。如果你读入的是带透明通道的PNG即RGBA四通道直接用255 - img会把透明通道也反转透明区域变成完全不透明。大多数情况下这不是你想要的效果。处理RGBA图时要么先把透明通道拆出来反转RGB三个通道后再合回去要么先把图转换成RGB格式再反白。我一般在流程第一步就把所有输入统一转换成RGB省得后面每个操作都要考虑通道数的问题。3. 调色板信息修改给图像“换颜料盘”3.1 索引色图像与调色板的关系这一步是四个操作里最容易让人困惑的因为现在大家接触到的照片几乎都是真彩色图压根没有调色板这个概念。要理解调色板得先搞明白“索引色图像”是怎么一回事。普通的真彩色图像比如最常见的24位BMP或JPEG每个像素直接保存RGB三个分量一个像素占3个字节。而索引色图像里每个像素保存的不是颜色本身而是一个编号。比如像素值是5意思就是“当前图像使用调色板上的第5号颜色”。调色板本身是一张颜色查找表按顺序定义了0号颜色、1号颜色、2号颜色直到255号颜色每一行的颜色都是一个RGB三元组。用个生活化的类比调色板就是你画画用的颜料盘像素值记录的是“你用的是第几格颜料”而不是颜料的实际颜色。调色板上1号格本来是红色你把1号格换成蓝色整张画里所有用1号格的区域就全部从红色变成蓝色但你不需要去改画面的任何一个点。这就是修改调色板信息最核心的特点像素值完全不变变的是颜色的映射关系。所以修改调色板信息之前先确认你的图像确实是索引色模式。在Pillow里图像模式是P才表示索引色。如果输入的是一张常规的JPEG照片它在内存里是RGB模式没有调色板可改。想改也行先用convert(P)把真彩色图量化成索引色图这个过程会自己生成一个调色板然后再修改它。要注意的是这个转换本身是有损的256色调色板只能表示256种颜色转完颜色会有一定偏差。3.2 修改调色板的具体操作用Pillow修改调色板的代码很简洁from PIL import Image img Image.open(palette.png) if img.mode ! P: img img.convert(P) palette img.getpalette() # 返回一个列表长度通常是768 # 列表顺序第0个是0号颜色的R第1个是0号颜色的G第2个是0号颜色的B # 第3个开始是1号颜色以此类推 # 示例把0号颜色改成纯红色 palette[0] 255 palette[1] 0 palette[2] 0 # 上面只改了3个值如果只想改一个颜色这样就够了 img.putpalette(palette) img.save(palette_modified.png)这里有一个需要注意的地方getpalette()返回的列表长度并不总是768。如果图像实际用到的颜色数少于256有些库实现会只返回实际颜色数对应的长度比如图像只有16种颜色返回的可能就是48个值16乘以3。但putpalette()需要完整的256色调色板也就是768个值。我的习惯是先检查长度不够768就补齐if len(palette) 768: palette.extend([0] * (768 - len(palette)))补的数值没有严格规定因为索引色图像里没有被用到的颜色索引补了也不影响显示。但我习惯补成灰色而不是黑色这样万一后面没注意某个索引显示的也是能看出来有问题的灰色而不是和背景黑色混淆。还有一种更优雅的做法是把调色板当成二维数组来处理import numpy as np palette img.getpalette() palette_arr np.array(palette).reshape(-1, 3) # 把10号颜色改成绿色 palette_arr[10] [0, 255, 0] # 改完再展平回列表 img.putpalette(palette_arr.flatten().tolist())这样按索引改颜色更清晰不会出现手数列表位置的尴尬。我用这个方法做一个批量实验把调色板前50个颜色全部改成某种颜色看看图像的变化其实就是把低亮度区域全部映射成一个颜色。效果有点类似抠图或者海报化但代码量非常小几行就做完了。3.3 换色实验通道交换与索引染色调色板改起来花样挺多的我在这里多分享两个玩法都是实际中用得上的思路。一个是通道交换。把调色板数组reshape成(N, 3)之后把第0列和第2列交换相当于把红色通道和蓝色通道互换图像立刻变成另一种色调。在真彩色图像上做通道交换你还要小心是RGB还是BGR顺序但在调色板里规则就一个第一列是R第二列是G第三列是B怎么改自己完全能控制。另一个玩法是给灰度图套伪彩色。灰度图转成P模式后把调色板设置成渐变颜色比如从黑色到红色再到黄色灰度图立刻变成热力图风格。医学图像里常用的伪彩色就是这种思路把灰度值映射成有颜色的调色板让细节更容易被肉眼区分。做法是# 先准备一个渐变色调色板 new_palette [] for i in range(256): t i / 255 new_palette [int(255 * t), 0, int(255 * (1 - t))] img.putpalette(new_palette)这段代码生成的调色板是从红色渐变到蓝色的给灰度图套上之后暗部偏蓝亮部偏红视觉区分度比单纯灰度图高很多。不过要留意伪彩色只是改变了显示映射并不会改变图像数据的亮度值你导出的txt里保存的仍然是灰度值。4. 彩色图像转灰度别用简单平均糊弄4.1 灰度化的原理与权重来源彩色图像转灰度看起来就是把R、G、B三个通道揉成一个值但怎么揉是有讲究的。最简单的想法是取三个通道的平均值gray (R G B) / 3。这样能得到灰度图但视觉效果往往偏暗、偏灰细节也不够清楚。原因在于人眼对三种颜色的敏感程度完全不同。人类的视觉系统对绿色最敏感因为视网膜上的感光细胞在绿色波段分布最密对红色敏感度中等对蓝色敏感度最低。如果三个通道平均分配权重相当于把本来就难看清的蓝色细节和最容易看清的绿色细节一视同仁结果就是图像失去了那种自然的明暗层次。这也是为什么标准灰度化公式里给绿色分配了最高权重。行业通用的标准是ITU-R BT.601建议的加权公式Gray 0.299 * R 0.587 * G 0.114 * B三个权重加起来刚好是1。0.587给了绿色0.299给了红色蓝色只有0.114。你直接用这个公式转出来的灰度图看起来最接近人眼对原始彩图的明暗感受暗部不会糊成一片亮部也不会出现过曝感。有一个细节值得注意公式里的0.299这些系数不是某个科学家拍脑袋定的而是从大量视觉实验里统计出来的。它代表的是“标准观察者”对三种颜色的相对亮度感知。所以实际工程里不需要自己调直接用现成系数就行。不少初学者会在网上看到另一个版本0.2126、0.7152和0.0722那是BT.709标准用于高清视频和sRGB色彩空间用在普通图像上也没大问题但两个标准不要混着用。我的建议是跟随你用的库OpenCV的COLOR_BGR2GRAY内部用的就是BT.601权重直接用就好。4.2 代码实现与效果对比用OpenCV转灰度代码是最简单的import cv2 img cv2.imread(input.png) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) cv2.imwrite(gray.png, gray)cvtColor一行就完成了但如果你想知道背后到底发生了什么或者课程设计里要求手写算法那可以手动实现一遍import cv2 import numpy as np img cv2.imread(input.png) # OpenCV读入的通道顺序是BGR不是RGB b img[:, :, 0].astype(np.float32) g img[:, :, 1].astype(np.float32) r img[:, :, 2].astype(np.float32) gray 0.299 * r 0.587 * g 0.114 * b gray gray.astype(np.uint8) cv2.imwrite(gray_manual.png, gray)这里必须强调一件事OpenCV的imread读入图像后通道顺序是BGR不是我们习惯的RGB。所以手动实现灰度化公式的时候取出来的第0通道是蓝色第2通道才是红色千万别写反。你写0.299 * b 0.587 * g 0.114 * r虽然运算顺序一样结果数值也一样因为这三个量是加在一起的权重是绑定的但如果你把权重跟通道对应错比如把0.299给了蓝通道那就大错特错了。结果会偏得一塌糊涂。为了验证效果我常用一个统一的办法把原图、平均值法、加权法生成的灰度图放在同一视图里对比。平均值法的图明显偏灰尤其在蓝色占比大的区域比如天空、水面会显得发暗发闷。加权法生成的图则更通透亮部层次更丰富。这个差异在色彩丰富的照片上更明显纯色块或者简单的图形上反而很难看出区别所以做验证时记得选一张颜色丰富的图。4.3 灰度化操作中容易犯的错灰度化这一步的坑不算多但有几个值得提一下。第一个坑是前面说过的BGR和RGB顺序混淆。不只是手动实现时容易错你在用Matplotlib显示图像时也会遇到。matplotlib默认按RGB顺序显示你把OpenCV读进来的BGR图直接imshow颜色会偏蓝红互换人脸会变得像外星人。解决方法是显示前用cv2.cvtColor(img, cv2.COLOR_BGR2RGB)转一次通道顺序。这件事跟灰度化本身无关但在整套流程中经常一起出现很多人会误以为是灰度化出了问题。第二个坑是图像本来就是灰度图你还去做灰度化。有些同学读入一张灰度图后没检查通道数直接调cvtColorOpenCV会报错因为输入只有单通道。处理办法是先用img.ndim检查维度如果是二维矩阵说明已经是灰度图了跳过这步。我习惯写一个判断分支让函数既能处理彩色输入也能处理灰度输入。第三个坑是数据类型被改成浮点后忘记转回去。手动实现灰度化时为了保留小数精度我把三个通道转成float32计算但计算完如果不转回uint8就保存OpenCV的imwrite会把浮点数按0到1的范围解释结果就是保存的照片几乎全白。保存前必须astype(np.uint8)。这跟反白那边的坑是一样的时刻清楚你的数据是什么类型、范围是多少。5. 图像数据导出txt让像素值变成“明文”5.1 txt文件里的数据组织方式把图像数据保存成txt听起来原始但实际场景里非常有用。比如你要把图像数据交给另一个程序处理像C语言或MATLAB写的小工具它们不一定方便直接读图片格式txt是通用性最好的交换格式。再比如你想直观地检查一张图某个区域的像素值分布用图片查看器是看不出来的导出成txt后直接搜数值就行。我还在做数据可视化的Demo时遇到过一种情况需要把图像数值导入到表格里画热力图图片文件没法直接用导出成txt再导入就要容易得多。txt文件本身没有固定的格式要求关键是定下一套规则让后续读取的人或程序能明确知道每个数字代表什么。我通常这样组织第一行可以写注释注明行列数和通道信息比如# shape: 240 320 3如果是灰度图每一行对应图像的一行像素像素之间用空格或逗号分隔如果是彩色图每一个像素的多个通道值写在一起用逗号隔开像素之间用分号或空格隔开我用得最多的是灰度图每行像素一行文本的格式比如12 34 56 78 90 23 45 67这样安排的好处是任何人拿到文件都能直观地看到图像矩阵的长相。保存出来之后还能顺便数一下行数一行就是图像的一行非常直观。彩色图稍微复杂一点我一般会让每个像素输出三个通道值并明确标注顺序。我用OpenCV处理时顺序是BGR但我导出的txt里我会在文件头部注释里写清楚顺序避免别人拿过去之后默认是RGB。5.2 二维灰度图和三维彩图的写入方案写txt文件的方法有两个层次简单粗暴的和推荐使用的。灰度图适合用numpy.savetxt性能好、代码少import numpy as np gray cv2.imread(gray.png, cv2.IMREAD_GRAYSCALE) np.savetxt(gray_data.txt, gray, fmt%d, delimiter )fmt%d表示按整数保存不会出现128.0000000000这种冗长的小数。delimiter 控制像素之间的分隔符。彩色图是三维矩阵np.savetxt不支持直接保存三维数组处理思路是把三维数据变换成二维再写。最直接的做法是把每个像素的三个通道拼接成一个字符串import cv2 img cv2.imread(input.png) h, w, c img.shape with open(color_data.txt, w, encodingutf-8) as f: f.write(f# shape: {h} {w} {c}\n) f.write(# channel order: B G R\n) for row in img: line .join( f{pixel[0]},{pixel[1]},{pixel[2]} for pixel in row ) f.write(line \n)这样每一行对应图像的一行像素每个像素的三个通道用逗号连在一起像素之间用空格隔开。后续解析的时候先按空格拆出像素再按逗号拆出通道。这种格式比把三通道铺开成三列更紧凑语义也更清楚。如果你想要的是纯矩阵、不带任何注释的版本用于程序自动读取那就不写注释头。两种格式各有用途给人看的加注释给程序读的不加。5.3 大图导出时的性能与精度控制先说性能。图像是二维矩阵一张1920乘1080的灰度图有207万多个数值如果你用Python的双层for循环逐像素写入txt会非常慢。我试过用最朴素的写法处理一张几百万像素的图能等上几十秒体验极差。原因在于Python每执行一次循环都有不小的解释器开销几百万次循环叠加起来就是灾难。解决办法很简单能向量化就不要循环能用内置函数就不要手写。np.savetxt内部是优化过的处理几百万个数值只要一秒钟左右。彩色图用上面那种生成器表达式的方式写也远比双层for循环快因为join和生成器在C层面做了大量优化但还是不如np.savetxt快。如果遇到特别大的图比如上亿像素的卫星影像我建议先对图像做降采样或者裁剪区域再导出否则txt文件本身也会大到几百MB普通文本编辑器根本打不开写出来也没意义。再讲精度控制。如果图像是uint8类型导出整数值就够了。但如果图像已经转成浮点类型比如归一化到0到1之间的浮点图直接保存会出现一长串小数。需要在写入前决定保留几位小数或者干脆乘255转回整数。我在处理灰度图时通常统一导出0到255的整数这样数据在任何程序里都好处理。如果你要保留浮点精度np.savetxt里可以用fmt%.6f控制小数位数。还有一个细节保存txt时如果要用Excel打开看建议使用逗号作为分隔符文件后缀改成.csv这样Excel双击就能直接打开成表格。但如果你是按课程要求交txt文件就按老师要求的格式来。我在自己的项目里一般黑白图用空格分隔的txt彩色图用逗号分隔的csv方便不同场景使用。6. 完整演示流程一张彩色图跑通全部操作前面把四个操作拆开讲了一遍现在把它们组合起来形成一个完整的可运行脚本。我习惯分成四个函数每个函数只做一件事然后在一个主流程里按顺序调用。这样后面想单独复用某个环节直接调用对应函数就行不用在长脚本里翻找。完整代码如下import cv2 import numpy as np from PIL import Image def invert_image(img): 反白像素值取反 if img.dtype ! np.uint8: img (img * 255).astype(np.uint8) return 255 - img def convert_to_gray(img): 彩色图转灰度兼容已经是灰度图的情况 if len(img.shape) 2: return img if img.shape[2] 4: img cv2.cvtColor(img, cv2.COLOR_BGRA2BGR) return cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) def modify_palette(input_path, output_path): 修改调色板把0号颜色改成红色 img Image.open(input_path) if img.mode ! P: img img.convert(P) palette img.getpalette() if len(palette) 768: palette.extend([0] * (768 - len(palette))) palette[0] 255 palette[1] 0 palette[2] 0 img.putpalette(palette) img.save(output_path) def save_gray_to_txt(gray_img, output_path): 灰度图导出txt np.savetxt(output_path, gray_img, fmt%d, delimiter ) if __name__ __main__: # 1. 读入彩色图 img cv2.imread(input.png) # 2. 反白 inverted invert_image(img) cv2.imwrite(inverted.png, inverted) # 3. 转灰度 gray convert_to_gray(inverted) cv2.imwrite(gray.png, gray) # 4. 灰度图导出txt save_gray_to_txt(gray, gray_data.txt) # 5. 调色板修改单独读入一张索引色图 modify_palette(palette.png, palette_modified.png)这个流程里有一个顺序上的考虑我先对原图做反白再对反白结果做灰度化这样灰度图在视觉上就是原图的负片效果跟原图的灰度和反白结果形成对比方便验证每一步是否真的生效。你也可以把顺序换成先灰度再反白结果数值上是等价的因为灰度化是线性加权反白是线性取反两个线性操作谁先谁后结果一样。但我在流程里选择先反白是想让灰度图能看出负片风格更容易确认“反白确实生效了”。关于中间文件的命名我的习惯是加上明确的前缀inverted.png、gray.png、gray_data.txt、palette_modified.png。这样不管过了多久回头看目录也能一眼看出每个文件的来源。如果输出文件多了最好再建一个output子目录统一存放别和源代码混在一起。这个习惯看着琐碎但等你需要反复调试、改参数的时候清晰的目录结构能省下大量时间。7. 常见问题与排查记录7.1 读图读到None路径与中文名最典型的报错是cv2.imread之后图像变量是None后面的代码一执行就报空指针错误。九成原因都是路径不对要么文件不在当前目录要么路径里包含中文。OpenCV在Windows上对包含中文的路径支持不太好我踩过很多次。解决方案有三种把文件路径里的中文改成英文用相对路径切到正确目录或者改用cv2.imdecode配合np.fromfile来读这样能正确处理中文路径。前两个方案是首选改个文件名就能解决的事没必要为它写额外代码。7.2 反白之后颜色怪怪的通道顺序在作怪用OpenCV读入再反白输出的PNG在Windows照片查看器里看颜色不对红色变成蓝色、蓝变成红这种情况几乎可以肯定是BGR和RGB的混淆。反白本身不会破坏通道关系但如果中间某一步把图像传给Pillow或者Matplotlib显示过它们默认按RGB解释颜色就对不上了。排查方法很简单把原图、反白图用同一种显示方式打开对比如果原图单独看颜色正常反白图单独看颜色不对且整体呈互补色调那就是通道顺序的锅。解决方法是统一通道顺序我通常以OpenCV的BGR为准保存前不再转换因为最终保存成PNG时OpenCV会正确处理。7.3 调色板改了却看不出变化先确认图像模式修改调色板后发现图像完全没变常见原因有两个。第一个图像本身不是P模式你是在RGB模式下做操作而RGB模式根本没有调色板修改操作被忽略了。解决方法是先打印img.mode确认再决定是否convert(P)。第二个保存格式不支持调色板。你把修改后的图像保存成JPEGJPEG本身是面向真彩色的格式保存时会丢弃调色板信息重新编码成RGB你改的颜色自然就“消失”了。保存时应选择PNG或GIF这两种格式原生支持调色板。我一般保存成PNG兼容性最好。7.4 保存的txt用Excel打开乱成一团用np.savetxt保存的txt默认用空格或制表符分隔Excel对这种格式的自动分列时而不灵打开后所有数值挤在一列里。解决方法有两个保存时用逗号分隔并改成.csv后缀Excel双击就能自动分列或者在Excel里用“数据—分列”功能选择按空格分列。如果你的数据后续要交给别的程序处理保持空格分隔的txt也没问题。这里再补充一个经验导出的txt里如果包含汉字注释某些文本编辑器会显示乱码解决办法是写入文件时指定encodingutf-8并且避免写中文表头英文注释最省心。7.5 输出数值整体偏大或偏小检查数据类型保存txt后发现所有数值都很大或者都是0和1大概率是数据类型问题。浮点图数值范围0到1时直接fmt%d保存全都会变成0因为整数格式把小数部分全丢了。此时应该用fmt%.6f或者先把数值乘255再转成uint8。反过来如果图像是16位深数值范围是0到65535直接保存整数值没问题但你在文本里看到大范围数值时别觉得奇怪先确认原图的位深就行。这套流程跑完我最大的感触就是数字图像处理里大部分算法本身不难难的是数据格式和类型转换。你在反白时要知道图像是uint8还是浮点在调色板操作时要知道图像有没有调色板在灰度化时要知道通道顺序在导出txt时要知道数值范围。这四个操作恰恰把最容易出问题的几个环节全都覆盖了。我每次跑这套流程都习惯把中间结果每步都输出一张图检查一遍再继续下一步这套习惯帮我避开了不少隐蔽的坑。你在自己做类似项目时也建议保持这个习惯反正输出一张图就是一行代码的事但调试时省下的时间可以说是相当可观。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑