使用OpenCV VideoWriter Python来提高视频图像质量

3

我正在尝试从二进制日志文件中读取的图像创建视频。然而,输出视频质量较低,但输出文件属性在图像大小、fps和持续时间等设置相同的情况下看起来很好。以下是我的代码。

目前,在调整大小之前,图像的大小为:

我希望图片大小设置没有问题。

我使用以下命令创建文件:

data.astype('int16').tofile(r"data1.out")

一旦有了 data,我执行以下步骤获取 img,因为 data 表示为每像素12位。


@DanMašek,我已经添加了原始图像。也许你现在可以帮我了? - Reddy2810
@Rotem,我已经添加了原始图像。也许你现在可以帮我了? - Reddy2810
1
@Reddy2810 这张图片看起来很奇怪(有明亮和暗的列)。你是怎么将它从12位转换为8位的?为什么要张贴JPEG而不是PNG? - Rotem
让我们在聊天中继续这个讨论 - Reddy2810
只是一个想法...每像素12位可能是带有子采样U和V的YUV格式,例如NV21。 - Mark Setchell
显示剩余12条评论
1个回答

2
看起来你解压缩这12位的方式不正确。
  • Lower 4 bits of mid_uint8 are the upper 4 bits of fst_uint12 (and fst_uint8 are the lower 8 bits).
    mid_uint8 ******** fst_uint8 ********
    fst_uint12 |||| ||||||||
    Code for unpacking fst_uint12:

     fst_uint12 = fst_uint8 + ((mid_uint8 & 0xF) << 8)
    
  • Upper 4 bits of mid_uint8 are the lower 4 bits of snd_uint12 (and lst_uint8 are the upper 8 bits).
    lst_uint8 ******** mid_uint8 ********
    snd_uint12 |||||||| ||||
    Code for unpacking snd_uint12:

     snd_uint12 = (lst_uint8 << 4) + (mid_uint8 >> 4)
    
构建完12位图像后,结果看起来像彩色滤镜阵列(Color Filter Array)图像。
这个CFA不是经典的Bayer滤镜,而是其他我无法识别的东西。
注意:我的解释可能是错误的,它可能根本不是彩色滤镜阵列。
这里有一段代码,它读取'data1.out',解包12位,并将其转换为BGR格式:
import numpy as np
import cv2

width, height = 1824, 992  # Image width and height.

data = np.fromfile('data1.out', np.uint16)  # Read data from file into 1D NumPy array as type uin16
data = data.astype(np.uint8)  # np.max(data) is 255, so I have to assume data is actually uint8 - convert data to uint8.

# Separate data into low, mid and high bytes - before unpacking 12 bits elements.
fst_uint8 = data[0::3].astype(np.uint16)  # Convert to uint16 (used at the next stage).
mid_uint8 = data[1::3].astype(np.uint16)
lst_uint8 = data[2::3].astype(np.uint16)

# Unpack first 12 bits:
# Lower 4 bits of mid_uint8 are the upper 4 bits of fst_uint12 (and fst_uint8 are the lower 8 bits).
# mid_uint8   ********   fst_uint8   ********
# fst_uint12      ||||               ||||||||
fst_uint12 = fst_uint8 + ((mid_uint8 & 0xF) << 8)

# Unpack second 12 bits:
# Upper 4 bits of mid_uint8 are the lower 4 bits of snd_uint12 (and lst_uint8 are the upper 8 bits).
# lst_uint8   ********   mid_uint8   ********
# snd_uint12  ||||||||               ||||
snd_uint12 = (lst_uint8 << 4) + (mid_uint8 >> 4)

# Interleave fst_uint12 and snd_uint12
data_uint12 = np.zeros(len(fst_uint12)*2)
data_uint12[0::2] = fst_uint12
data_uint12[1::2] = snd_uint12

# Reshape data_uint12 into img
img = np.reshape(data_uint12, (height, width))

# Convert to uint8 - simply divide by 16 (loose some accuracy, but its good enough for getting a sample).
img = (img//16).astype(np.uint8)

# Apply demosaic - not sure it is correct.
bgr_img = cv2.cvtColor(img, cv2.COLOR_BAYER_GB2BGR)

bgr_img = cv2.resize(bgr_img, (912, 496)) # Resize bgr_img

# Show images for testing
cv2.imshow('img', img)
cv2.imshow('bgr_img', bgr_img)
cv2.waitKey()
cv2.destroyAllWindows()

结果:

bgr_img

由于OP的请求,图像已被移除。

img

由于OP的请求,图像已被移除。


应用对比度增强:
线性对比度增强示例(参见:使用百分比进行对比度增强)。
# Reshape data_uint12 into img
img = np.reshape(data_uint12, (height, width))

# Crop relevant ROI
img = img[40:978, 100:1714]

# Apply linear "stretch" - lo goes to 0, and hi goes to 1
lo, hi = np.percentile(img, (1, 99))  # 1% - Low percentile, 99% - High percentile
stretch_img = (img.astype(float) - lo) / (hi-lo)
stretch_img = np.maximum(np.minimum(stretch_img*255, 255), 0).astype(np.uint8)  # Multiply by 255, clamp range to [0, 255] and convert to uint8

# Apply demosaic - not sure it is correct.
stretch_bgr = cv2.cvtColor(stretch_img, cv2.COLOR_BAYER_GB2BGR)
stretch_bgr = cv2.resize(stretch_bgr, (912, 496)) # Resize bgr_img

CLAHE的示例(应用于灰度图像):

# Create a CLAHE object (Arguments are optional).
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
cl1 = clahe.apply(cv2.cvtColor(stretch_bgr, cv2.COLOR_BGR2GRAY))  # Convert BGR to gray-scale and apply contrast enhancement.

结果:

stretch_bgr
由于 OP 请求,图片已被删除。

cl1
由于 OP 请求,图片已被删除。


更新:

正在处理RCCC彩色滤镜阵列:

正如Dan Mašek所评论的,原始图像格式应用RCCC彩色滤镜阵列

enter image description here

具体的顺序是:
CR
CC
(红色通道位于每个2x2像素的右上角)。
我们可以根据以下论文重建“清晰通道”(亮度):工程师笔记
# Reshape data_uint12 into img
img = np.reshape(data_uint12, (height, width))


# The CFA is RCCC
# Color Filter Array ordering:
# CR
# CC

img = np.reshape(data_uint12, (height, width)).astype(np.uint16)

img = img[40:978, 100:1714]  # Crop relevant ROI

# Reconstruct C (clear pixel value), in position of red pixels.
# Apply convolution as described here:
# https://www.analog.com/media/en/technical-documentation/application-notes/EE358.pdf

k = np.array([[ 0,  0, -1,  0,  0],
              [ 0,  0,  2,  0,  0],
              [-1,  2,  4,  2, -1],
              [ 0,  0,  2,  0,  0],
              [ 0,  0, -1,  0,  0]], float) * (1.0/8.0)

tmp_img = cv2.filter2D(img, -1, k)  # Convolve image with kernel k
tmp_img = np.minimum(tmp_img, 4095)  # Limit result to valid range of 12 bits.

# Image of "clear" pixels - replace original "red" pixels with values of red pixels after filter2D.
c_img = img.copy()
c_img[0::2, 1::2] = tmp_img[0::2, 1::2]

cv2.imshow('c_img', c_img*16)  # Show image for testing
cv2.waitKey()

cv2.imwrite('c_img.png', cv2.resize((c_img//16).astype(np.uint8), (912, 496)))

修复颜色: 由于只有透明像素和红色像素,因此我们无法重建绿色和蓝色的颜色。 下面的代码构建了一个“假彩色”红色图像。 “红色图像”不是红色的精确重建,它只是类似于图像应该看起来的样子(假设只有红色和透明通道)。
为了构建图像,我使用了以下阶段:
- 通过调整大小提取红色通道 - 假设足够好。 - 从清晰和红色通道计算绿色和蓝色通道(假设绿色和蓝色相等)。 - 将BGR转换为YCrCb颜色格式。 - 在Cb通道中放置128的值(消除蓝色)。 - 对Y通道应用CLAHE对比度增强。 - 从YCrCb转换回BGR。
以下是代码:
# Extract red color channel by resizing - assume it's good enough.
r_img = cv2.resize(img[0::2, 1::2], (img.shape[1], img.shape[0]))


# In YUV color space: Y = 0.2126*R + 0.7152*G + 0.0722*B
# We know Y (Y applies clear channel) and R, but we don't know G and B.
# For simplicity, assume G = B.
# Y = 0.2126*R + (0.7152+0.0722)*G ==> G = (Y - 0.2126*R)/(0.7152+0.0722) and B = G
g_img = c_img - 0.2126*r_img / (0.7152+0.0722)
b_img = g_img
tmp_bgr_img = (np.dstack((b_img, g_img, r_img))//16).astype(np.uint8)  # Merge channels and convert to uint8

# Convert BGR to YCrCb
ycrcb_img = cv2.cvtColor(tmp_bgr_img, cv2.COLOR_BGR2YCrCb)

# Cr approximates red color hue
# Cb approximates blue color hue
# Place 128 in Cb, because there is no blue color (keep only Cr)
ycrcb_img[:, :, 2] = 128

# Apply CLAHE enhancement on Y channel - remark: the conventional way is converting BGR to LAB and apply CLAHE on L.
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
ycrcb_img[:, :, 0] = clahe.apply(ycrcb_img[:, :, 0])
    
# Convert back to BGR - build image with gray and red colors:
reddish_bgr_img = cv2.cvtColor(ycrcb_img, cv2.COLOR_YCrCb2BGR)

cv2.imshow('reddish_bgr_img', reddish_bgr_img)  # Show image for testing
cv2.waitKey()

cv2.imwrite('reddish_bgr_img.png', cv2.resize(reddish_bgr_img, (912, 496)))

结果:

图片已因发帖人请求而被删除。


谢谢@Rotem,这已经好多了,但是能否提高颜色质量,使场景中的交通标志或车辆更亮更清晰可见? - Reddy2810
是的,有可能在不降低图像质量的情况下获取彩色图像。 - Reddy2810
请查看聊天记录(正如 OP 建议的几次)。我非常有信心这是一个 RCCC 数组。 - Dan Mašek
我认为我展示了一种更简单的方式来解开那里的数据…只需使用32位整数即可。然后,您只需要适当地移动输入,然后移动/掩码获取12位片段。| 既然你已经写好了这个,随意使用我在聊天中描述的内容来改进你的答案(如果你感觉合适的话,也可以进行归属);)。 - Dan Mašek
1
@DanMašek 谢谢你澄清这个问题。我相当有信心这也是一个 RCCC。 - Rotem
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接