ISP之DPC(坏点校正)模块

DPC - Defective Photosites Correction即坏点校正

一、DPC的意义

图像传感器上由于自身工艺技术造成的瑕疵,如光线采集的点存在缺陷,导致在光电转换过程中某些像素值不准确,我们称之为坏点(Defect Pixel)。尤其是对于低成本的sensor来说,坏点数为100 或者1000ppm(parts per million,百万分之一)是正常的。若sensor中存在坏点,经过图像的插值(如demosaic)和滤波等非线性过程,坏点的尺寸会变大(坏点扩散),而且由于色彩校正和串扰补偿,坏点处颜色的强度和饱和度也会明显提高,因此需要在其他模块执行之前对坏点进行校正,坏点一般分为静态坏点和动态坏点。

  • 静态坏点:
    • 亮点:亮点的亮度值明显大于入射光乘以相应比例,随着曝光时间的增加,该点的亮度会显著增加;
    • 暗点:接近于0。
  • 动态坏点:在一定像素范围内,该点表现正常,而超过这一范围,该点表现的比周围像素要亮。动态坏点与sensor 温度、gain值有关,sensor 温度升高或者gain 值增大时,动态坏点会变的更加明显。

坏点的存在一方面会影响图像的画质,另一方面,在某些应用场景下可能会影响CV的识别准确率,因此一般需要对DPC进行矫正。但是DPC的矫正也会带来相应的副作用,例如锐度下降,图像细节损失等,所以进行合适的DPC矫正是必要的。

二、DPC一般怎么实现

  • 依赖标定的方法 - 主要校正静态坏点

静态坏点的校正是基于已有的静态坏点表(静态坏点表一般由sensor厂商提供),比较当前点的坐标是否与静态坏点表中的某个坐标一致,若一致则判定为坏点,然后再采用中值滤波的方法对其进行校正。一般来说,每颗sensor的静态坏点都可以百分百矫正,但这样做对sensor厂商会增加成本,所以对于低成本的sensor来说,由于成本和时间的限制,sensor制造商没有检测sensor的坏点信息,因此需要用户自行对静态坏点进行标定,得到静态坏点表。

  • 不依赖标定的方法 - 主要校正动态坏点

由于存储坏点信息的内存有限,静态坏点校正有可能无法完全校正sensor中的坏点,因此静态坏点校正应当校正对图像质量影响较大的坏点,而动态坏点校正方法则校正其余的坏点。动态坏点的校正可以实时的检测和校正sensor 的亮点与暗点,并且校正的坏点个数不受限制。动态坏点校正相对静态坏点校正具有更大的不确定性。动态坏点校正可以分为两个步骤:坏点检测和坏点校正。

三、代码实现

1、读取RAW图像文件

由于我自己采集到的RAW文件是.dng格式而不是.raw格式,在读取的时候也遇到来一些问题,所以先跟大家分享一下raw图的基本知识。

RAW文件是一个数据包文件,而非图像文件。我们的电脑并不知道应该如何解读这种数据包,和JPEG等标准格式不同的是,RAW文件并不包含供电脑解码所需的信息。

和JPEG格式不同的是,RAW格式并没有一个统一的标准。各大厂各行其是,使用的RAW文件格式各不相同,甚至同一厂商不同型号相机之间使用的标准也不一样。

DNG格式是这种混乱局面中的唯一例外,这是一种由Adobe开发的公开文件格式,可供任意数码相机制造商选用。

每一幅DNG图像是以8字节的IFH( Image File Header)开始的,所以说文件中不仅仅包含各像素点的值,还包含一些头文件等信息,我们在去读的时候需要剪掉头文件,仅取出来像素点的值。

由于博主自己采集到的图像坏点较少,为了让大家更清楚的看到坏点校正效果所以又手动添加了一些坏点。

用代码实现:

 
img = np.fromfile(r'/Users/Desktop/IMG_20230719_111556.dng',dtype = 'uint16')
a = len(img)
#原图大小为6016*4512
b = 6016 * 4512
#dng与raw的差别是,dng有头部信息,需要剪掉头部信息才是真实像素值,原图为10bit图像
img1 = img[a-b:a].reshape(4512,6016)
# 转化为8bit图像
img2 = img1/4#添加坏点
img3 = img2.copy()for i in range(img3.shape[0]):for j in range(img3.shape[1]):if random.random() < prob:img3[i][j] = 0 if random.random() < 0.0001 else 255else:img3[i][j] = img3[i][j]img3.astype(int)

2、校正坏点

2.1 中值滤波

坏点的分布符合我们图像中椒盐噪声的表现(噪声点的灰度值 与邻域像素点具有明显不同,因此在图像中造成过亮或 过暗的像素点, 严重影响图像的视觉质量),所以用中值滤波可以很有效的校正坏点。

用代码直接调用opencv也非常简单,下面是用一个5*5的滤波器进行中值滤波。

中值滤波的基本原来是:用一个X*X(X为奇数)的滤波器在画面中进行滤波,用滤波器中X*X点的中值代替(i,j)的值,实现坏点校正。

img4 = cv2.medianBlur(np.uint8(img3),5)

2.2识别坏点并校正坏点

中值滤波去除坏点简单有效,但是会存在一个问题,如果图像中并没有坏点,但是经过中值滤波后反而会损失细节。

如何避免细节的损失呢,我们可以用先识别坏点,然后再去除坏点的方法,尽可能的保留纹理。

  • 坏点识别
    • 图像中某像素坐标为(i, j)
    • 用(i,j)和它周围的8个像素比较
    • 若(i,j)与周围8个像素点的差异均大于1,那么判定该点为坏点
  • 考虑梯度的坏点校正
    • 计算2*(i,j)与水平、垂直及两条对角线的差值(dv,dh,ddl,ddr)
    • 判断边缘,比较(dv,dh,ddl,ddr)中最小的值
    • 为(i,j)赋值
    #考虑边缘:识别坏点并校正坏点thred = 1t = 0img4 = img3.copy()#坏点识别for i in range(1, img4.shape[0] - 1):for j in range(1, img4.shape[1] -1):P = img4[i-1:i+2,j-1:j+2].copy()different_value = np.abs(P - P[1,1])          compare = (different_value / P2) > thrednumber = np.count_nonzero(compare)if number == 8:dv = abs(2 * P[1,1] - P[0,1] - P[2,1])dh = abs(2 * P[1,1] - P[1,0] - P[1,2])ddl = abs(2 * P[0,0] - P[0,0] - P[2,2])ddr = abs(2 * P[1,1] - P[2,0] - P[0,2])t = t + 1if (min(dv, dh, ddl, ddr) == dv):img4[i, j] = (P[0,1] + P[2,1]) / 2elif (min(dv, dh, ddl, ddr) == dh):img4[i, j] = (P[1,0] + P[1,2]) / 2elif (min(dv, dh, ddl, ddr) == ddl):img4[i, j] = (P[0,0] + P[2,2]) / 2else:img4[i, j] = (P[0,2] + P[2,0]) / 2

四、结果

坏点图片

DPC后图片 


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部