rawpy坏点修复 && ExifRead获取RAW曝光、ISO、光圈等exif信息


1.1 读取RAW并保存为RGB图片

# csdn -牧野- 2020-3-20
import rawpy
import imageio
import numpy as np

raw = rawpy.imread('take.dng')

# use_camera_wb 是否执行自动白平衡,如果不执行白平衡,一般图像会偏色
# half_size 是否图像减半
# no_auto_bright 不自动调整亮度
# output_bps bit数据, 8或16
img = raw.postprocess(use_camera_wb=True, half_size=False, no_auto_bright=False, output_bps=16)

# img = np.float32(img / (2**16-1)*255.0)
# img = np.asarray(img,np.uint8)

imageio.imsave('take.jpg', img)

1.2 读取RAW并保存为tiff

# csdn -牧野- 2020-5-20
import rawpy
import imageio
import numpy as np
import tifffile

raw = rawpy.imread('take.dng')

# use_camera_wb 是否执行自动白平衡,如果不执行白平衡,一般图像会偏色
# half_size 是否图像减半
# no_auto_bright 不自动调整亮度
# output_bps bit数据, 8或16
img = raw.postprocess(use_camera_wb=True, half_size=False, no_auto_bright=True, output_bps=16)

# img = np.float32(img / (2**16-1)*255.0)
# img = np.asarray(img,np.uint8)

tifffile.imwrite('take.tiff', data=img)

 

2. 修复RAW坏点

利用多张RAW文件查找出错误像素并修复

# csdn -牧野- 2020-3-20
import rawpy
import imageio
import rawpy.enhance

# 具有同样坏点的3~5张图像,最好是不同场景下拍的
# 基本上,图像越多,检测到的坏点会越少
paths = ['1.dng', '2.dng', '3.dng', '4.dng']

bad_pixels = rawpy.enhance.find_bad_pixels(paths)

raw = rawpy.imread('1.dng')
rawpy.enhance.repair_bad_pixels(raw, bad_pixels, method='median')
img = raw.postprocess(use_camera_wb=True, half_size=False, no_auto_bright=False, output_bps=16)
imageio.imsave('1_repair_bed_pixels.jpg', img)

raw = rawpy.imread('1.dng')
img = raw.postprocess(use_camera_wb=True, half_size=False, no_auto_bright=False, output_bps=16)
imageio.imsave('1_without_repair.jpg', img)

 

3.1 bayer阵列解析

拜耳阵列(Bayer pattern)分为GBRG、GRBG、BGGR、RGGB四种模式:


使用rawpy把不同模式下raw解析为四通道:

# csdn -牧野- 2020-3-27
# 参考 https://www.cnblogs.com/thisisajoke/p/10418817.html
import rawpy
import numpy as np
from PIL import Image
import imageio
import exifread

def gray_ps(rgb):
    return np.power(np.power(rgb[:, :, 0], 2.2) * 0.2973 + np.power(rgb[:, :, 1], 2.2) * 0.6274
                    + np.power(rgb[:, :, 2], 2.2) * 0.0753, 1 / 2.2) + 1e-7

def do_HDR(x, curve_ratio):
    gray_scale = np.expand_dims(gray_ps(x), axis=-1)
    gray_scale_new = np.power(gray_scale, curve_ratio)
    return np.minimum(x * gray_scale_new / gray_scale, 1.0)

def adjust_out_matrix(RAW_path, out=None):
    raw = open(RAW_path, 'rb')
    exif_info = exifread.process_file(raw, details=False, strict=True)
    orientation_str = 'EXIF Orientation'
    if exif_info.__contains__('Image Orientation'):
        orientation_str = 'Image Orientation'
    orientation_info = exif_info[orientation_str].printable
    if orientation_info == 'Rotated 180':
        if out is None:
            return True
        else:
            if out.shape[2] == 3:
                out0 = out[:, :, :1]
                out1 = out[:, :, 1:2]
                out2 = out[:, :, 2:3]
                out = np.concatenate((out2, out1, out0), 2)
            elif out.shape[2] == 4:
                out0 = out[:, :, :1]
                out1 = out[:, :, 1:2]
                out2 = out[:, :, 2:3]
                out3 = out[:, :, 3:4]
                out = np.concatenate((out3, out2, out1, out0), 2)
            else:
                raise
            return np.flip(out)

    elif orientation_info == 'Horizontal (normal)':
        if out is None:
            return False
        else:
            return out
    else:
        raise

# pack Bayer image to 4 channels
def pack_raw(raw_path, white_balance=True, auto_bright=True, HDR=True, save_JPEG=True):
    raw = rawpy.imread(raw_path)
    im = raw.raw_image_visible.astype(np.float32)

    # subtract the black level
    # 16383(2^14) is the camera's maximal pixel value, you can get it by "np.max(raw.raw_image)" . Ensure full exposure!
    im = np.maximum(im - raw.black_level_per_channel[0], 0) / (16383 - raw.black_level_per_channel[0])

    im = np.expand_dims(im, axis=2)
    H = im.shape[0]
    W = im.shape[1]

    if raw.raw_pattern[0, 0] == 0:  # RGGB
        out = np.concatenate((im[0:H:2, 0:W:2, :],
                              im[0:H:2, 1:W:2, :],
                              im[1:H:2, 1:W:2, :],
                              im[1:H:2, 0:W:2, :]), axis=2)
    elif raw.raw_pattern[0, 0] == 2:  # BGGR
        out = np.concatenate((im[1:H:2, 1:W:2, :],
                              im[0:H:2, 1:W:2, :],
                              im[0:H:2, 0:W:2, :],
                              im[1:H:2, 0:W:2, :]), axis=2)
    elif raw.raw_pattern[0, 0] == 1 and raw.raw_pattern[0, 1] == 0:  # GRBG
        out = np.concatenate((im[0:H:2, 1:W:2, :],
                              im[0:H:2, 0:W:2, :],
                              im[1:H:2, 0:W:2, :],
                              im[1:H:2, 1:W:2, :]), axis=2)
    elif raw.raw_pattern[0, 0] == 1 and raw.raw_pattern[0, 1] == 2:  # GBRG
        out = np.concatenate((im[1:H:2, 0:W:2, :],
                              im[0:H:2, 0:W:2, :],
                              im[0:H:2, 1:W:2, :],
                              im[1:H:2, 1:W:2, :]), axis=2)
    if white_balance:
        wb = np.array(raw.camera_whitebalance, np.float32)
        wb[3] = wb[1]
        wb = wb / wb[1]
        out = np.minimum(out * wb, 1.0)
    if auto_bright:
        mean_G = (out[:, :, 1].mean() + out[:, :, 3].mean()) / 2.0
        out = np.minimum(out*0.2/mean_G, 1.0)
    out = adjust_out_matrix(raw_path, out)
    if save_JPEG:
        out0 = out[:, :, 0:1]
        out1 = out[:, :, 1:2]
        out2 = out[:, :, 2:3]
        out3 = out[:, :, 3:4]
        out_JPEG = np.concatenate((out0, (out1 + out3) / 2., out2), axis=2)
        if HDR:
            out_JPEG = do_HDR(out_JPEG, 0.35)
        Image.fromarray(np.uint8(out_JPEG * 255)).save('result.jpg')
    return out

if __name__ == '__main__':
    raw = rawpy.imread('leica.dng')
    np_channel = pack_raw('leica.dng', auto_bright=False, HDR=False)
    img = raw.postprocess(use_camera_wb=True, half_size=False, no_auto_bright=True, output_bps=16)
    imageio.imsave('rawpy.jpg', img)

 

3.2 raw数据调整

# csdn -牧野- 2020-6-20
# 参考 https://github.com/letmaik/rawpy/issues/54
import rawpy
import numpy as np
import imageio

def adjust_raw(raw_path):
    raw_source = rawpy.imread(raw_path)
    raw_adjust = rawpy.imread(raw_path)
    H, W = raw_adjust.raw_image_visible.astype(np.float32).shape
    raw_adjust.raw_image_visible[:] = raw_adjust.raw_image_visible * 2

    img_source = raw_source.postprocess(use_camera_wb=True, half_size=False, no_auto_bright=True, output_bps=16)
    img_adjust = raw_adjust.postprocess(use_camera_wb=True, half_size=False, no_auto_bright=True, output_bps=16)

    imageio.imsave('raw_source.jpg', img_source)
    imageio.imsave('raw_adjust.jpg', img_adjust)

if __name__ == '__main__':
    adjust_raw('leica.dng')

 

4. raw 模式转换

# csdn -牧野- 2020-6-12
# 参考 https://github.com/Jiaming-Liu/BayerUnifyAug
import numpy as np
import rawpy

BAYER_PATTERNS = ["RGGB", "BGGR", "GRBG", "GBRG"]
NORMALIZATION_MODE = ["crop", "pad"]

def bayer_unify(raw: np.ndarray, input_pattern: str, target_pattern: str, mode: str) -> np.ndarray:
    """
    Convert a bayer raw image from one bayer pattern to another.
    Parameters
    ----------
    raw : np.ndarray in shape (H, W)
        Bayer raw image to be unified.
    input_pattern : {"RGGB", "BGGR", "GRBG", "GBRG"}
        The bayer pattern of the input image.
    target_pattern : {"RGGB", "BGGR", "GRBG", "GBRG"}
        The expected output pattern.
    mode: {"crop", "pad"}
        The way to handle submosaic shift. "crop" abandons the outmost pixels,
        and "pad" introduces extra pixels. Use "crop" in training and "pad" in
        testing.
    """
    if input_pattern not in BAYER_PATTERNS:
        raise ValueError('Unknown input bayer pattern!')
    if target_pattern not in BAYER_PATTERNS:
        raise ValueError('Unknown target bayer pattern!')
    if mode not in NORMALIZATION_MODE:
        raise ValueError('Unknown normalization mode!')
    if not isinstance(raw, np.ndarray) or len(raw.shape) != 2:
        raise ValueError('raw should be a 2-dimensional numpy.ndarray!')

    if input_pattern == target_pattern:
        h_offset, w_offset = 0, 0
    elif input_pattern[0] == target_pattern[2] and input_pattern[1] == target_pattern[3]:
        h_offset, w_offset = 1, 0
    elif input_pattern[0] == target_pattern[1] and input_pattern[2] == target_pattern[3]:
        h_offset, w_offset = 0, 1
    elif input_pattern[0] == target_pattern[3] and input_pattern[1] == target_pattern[2]:
        h_offset, w_offset = 1, 1
    else:  # This is not happening in ["RGGB", "BGGR", "GRBG", "GBRG"]
        raise RuntimeError('Unexpected pair of input and target bayer pattern!')

    if mode == "pad":
        out = np.pad(raw, [[h_offset, h_offset], [w_offset, w_offset]], 'reflect')
    elif mode == "crop":
        h, w = raw.shape
        out = raw[h_offset:h - h_offset, w_offset:w - w_offset]
    else:
        raise ValueError('Unknown normalization mode!')

    return out


def pack_raw(raw_path):
    raw = rawpy.imread(raw_path)
    im = raw.raw_image_visible.astype(np.float32)

    # subtract the black level
    # 16383(2^14) is the camera's maximal pixel value, you can get it by "np.max(raw.raw_image)" . Ensure full exposure!
    im = np.maximum(im - raw.black_level_per_channel[0], 0) / (16383 - raw.black_level_per_channel[0])
    return im

if __name__ == '__main__':
    raw_pack = pack_raw('0.dng')
    print(raw_pack.shape)
    raw_pack_new = bayer_unify(raw_pack, 'RGGB', 'GRBG', 'crop')
    print(raw_pack_new.shape)

 

5. ExifRead获取exif信息

ExifRead安装:

pip install exifread -i https://pypi.mirrors.ustc.edu.cn/simple/

# csdn -牧野- 2020-3-26
import exifread

# 不限于RAW,RGB图片也适用
raw_file = open('take.dng', 'rb')

exif_file = exifread.process_file(raw_file, details=False, strict=True)

for key, value in exif_file.items():
    print(key, value)

if exif_file.has_key('EXIF ExposureTime'):
    exposure_str = exif_file['EXIF ExposureTime'].printable
else:
    exposure_str = exif_file['Image ExposureTime'].printable
if '/' in exposure_str:
    fenmu = float(exposure_str.split('/')[0])
    fenzi = float(exposure_str.split('/')[-1])
    exposure = fenmu / fenzi
else:
    exposure = float(exposure_str)

if exif_file.has_key('EXIF ISOSpeedRatings'):
    ISO_str = exif_file['EXIF ISOSpeedRatings'].printable
else:
    ISO_str = exif_file['Image ISOSpeedRatings'].printable
if '/' in ISO_str:
    fenmu = float(ISO_str.split('/')[0])
    fenzi = float(ISO_str.split('/')[-1])
    ISO = fenmu / fenzi
else:
    ISO = float(ISO_str)
# FNumber = float(exif_file['Image FNumber'].printable)

print('曝光时间:', exposure)
# print('光圈:', FNumber)
print('ISO:', ISO)

 

  • 6
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
在Python中,可以使用一些图像处理库来修复曝光问题。其中一个常用的方法是使用中值滤波器来修复图像中的坏点。以下是一个示例代码,展示了如何使用中值滤波器修复图像中的坏点: 引用\[2\]:1)导入依赖库 import matplotlib.pyplot as plt import scipy.ndimage from scipy.ndimage import median_filter import numpy as np 引用\[1\]:4)显示修复结果 hot_pixels, fixed_image = find_death_pixels_and_fix(Z) plt.figure(figsize=(10, 5)) ax1 = plt.subplot(121) plt.axis('off') ax2 = plt.subplot(122) plt.axis('off') ax1.set_title('Raw data with death pixels') ax1.imshow(Z, interpolation='nearest', origin='lower') for y, x in zip(hot_pixels\[0\], hot_pixels\[1\]): ax1.plot(x, y, 'ro', mfc='none', mec='r', ms=10) ax1.set_xlim(0, 200) ax1.set_ylim(0, 200) ax2.set_title('Mean Filter:dead pixels removed') ax2.imshow(fixed_image, interpolation='nearest', origin='lower', clim=(0, 255)) plt.show() 首先,导入所需的库,包括matplotlib.pyplot、scipy.ndimage和numpy。然后,使用中值滤波器对图像进行处理。中值滤波器可以通过将每个像素的值替换为其周围像素的中值来平滑图像。这样可以有效地去除图像中的坏点。 在示例代码中,函数find_death_pixels_and_fix(Z)用于找到图像Z中的坏点,并返回修复后的图像fixed_image以及坏点的位置hot_pixels。然后,使用matplotlib.pyplot库来显示原始图像和修复后的图像。在原始图像中,坏点用红色圆圈标记。 请注意,图像坏点与图像噪声有很大区别。图像坏点是由图像传感器缺陷导致的,其位置是固定的,不受温度影响,对感光度和曝光时间不敏感。而图像噪声具有随机性,对温度、感光度和曝光时间很敏感。因此,图像坏点修复方法主要针对固定位置的坏点进行修复。 希望这个示例代码可以帮助你理解如何在Python中进行曝光修复。如果你有任何进一步的问题,请随时提问。 #### 引用[.reference_title] - *1* *2* *3* [数字图像处理与Python实现-图像信号处理-图像坏点修复](https://blog.csdn.net/wujuxKkoolerter/article/details/124909019)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值