前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >图像处理: jpg格式 存储-读写 时 像素值 微小变化 探究

图像处理: jpg格式 存储-读写 时 像素值 微小变化 探究

作者头像
JNingWei
发布2018-09-28 14:27:26
8970
发布2018-09-28 14:27:26
举报

起因

遇到问题

在做项目的过程中,想比较 同一幅图像二值化处理结果人工标注的ground_truth图 之间的差异。

因为这两幅用来比较的图在生成的时候都是 二值图像(即像素值非 0 即 255),所以用来求差异图的代码段,我想当然地这么写:

    for i in range(h):
        for j in range(w):
            if thresh_pic[i, j, 0] == 0 and label_pic[i, j, 0] == 0:
                diff_pic[i, j, :] = 0
            elif thresh_pic[i, j, 0] == 255 and label_pic[i, j, 0] == 255:
                diff_pic[i, j, :] = 255
            elif thresh_pic[i, j, 0] == 0 and label_pic[i, j, 0] == 255:
                diff_pic[i, j, 2] = 255
            elif thresh_pic[i, j, 0] == 255 and label_pic[i, j, 0] == 0:
                diff_pic[i, j, 1] = 255
            else:
                logging.error('({},{}): thresh={}, label={}'.format(i, j, thresh_pic[i, j, 0], label_pic[i, j, 0]))

在差异比对效果图出来之后却看到了很诡异的现象,图片中出现了很多黑色麻花,如下图:

这里写图片描述
这里写图片描述

logging打印出来的结果也显示有很多 既非 0 也非 255 的像素点:

...
ERROR:root:(279,535): thresh=254, label=0
ERROR:root:(279,576): thresh=253, label=0
ERROR:root:(279,581): thresh=253, label=0
...

修改代码

经过观察,我发现某些像素点在 存储为图片 之前 像素值 还是 255 或 0,存为图片 以后,就会变成了245~255或0~10范围内的随机数(在不懂原理的我看来感觉那就是随机偏移,真实情况其实应该是按照某个算法进行了对应的偏移)了。初步猜测是在 存储为图片时从图片读取出来时部分像素点 发生了 像素值 的 少许偏移

于是我修改了代码,考虑到了一个 阈值为10像素值偏移区间

    for i in range(h):
        for j in range(w):
            if thresh_pic[i, j, 0] < 10 and label_pic[i, j, 0] < 10:
                diff_pic[i, j, :] = 0
            elif thresh_pic[i, j, 0] > 245 and label_pic[i, j, 0] > 245:
                diff_pic[i, j, :] = 255
            elif thresh_pic[i, j, 0] < 10 and label_pic[i, j, 0] > 245:
                diff_pic[i, j, 2] = 255
            elif thresh_pic[i, j, 0] > 245 and label_pic[i, j, 0] < 10:
                diff_pic[i, j, 1] = 255
            else:
                logging.error('({},{}): thresh={}, label={}'.format(i, j, thresh_pic[i, j, 0], label_pic[i, j, 0]))

这次黑色麻花没有了,logging也没有打印error信息了:

这里写图片描述
这里写图片描述

查找资料

经过上网查找,我发现原来一些细心的前辈们也发现这个问题了,并给出了解答

这里写图片描述
这里写图片描述

Soga,原来是因为:

  • .jpg 是 有损压缩格式,保存时会 压缩失真 ( .png 是 无损压缩格式) 。

那么好奇心大发作的我又想拿我最爱的妹子图来进一步探究一下。

实验

实验思路

  1. 将原图像 复制多份 ,分别 进行 不同轮次 的 循环存储-读写,经过 多轮次 的 循环 后,在 肉眼层面 查看 新图像 是否明显较 原图像 有失真
  2. 比较 每一轮 循环存储-读写 后,图片上 各像素点 的 像素值 发生了哪些 变化(置色方案参见下表)。

像素点的像素值变化

置色方案

不变

黑色

增加

绿色

减少

红色

实验效果

图像:

这里写图片描述
这里写图片描述

100轮 存-读 之后的图像:

这里写图片描述
这里写图片描述

原图像第1轮 存-读 后的 图像差异

这里写图片描述
这里写图片描述

第1轮 存-读 后的图像 与 第2轮 存-读 后的 图像差异

这里写图片描述
这里写图片描述

第2轮 存-读 后的图像 与 第3轮 存-读 后的 图像差异

这里写图片描述
这里写图片描述

第3轮 存-读 后的图像 与 第4轮 存-读 后的 图像差异

这里写图片描述
这里写图片描述

第4轮 存-读 后的图像 与 第5轮 存-读 后的 图像差异

这里写图片描述
这里写图片描述

实验代码

# coding=utf-8

first_path = './data/first.jpg'
second_path = './data/second.jpg'
third_path = './data/third.jpg'
forth_path = './data/forth.jpg'
fifth_path = './data/fifth.jpg'

one_hundred_path = './data/one_hundred.jpg'

import cv2
import numpy as np
import logging

# 生成并保存 俩图像 的 差异图
def compare(pic1, pic2, dst_path):
    h, w, c = pic1.shape
    diff_pic = np.zeros_like(pic1, dtype=np.uint8)
    for i in range(h):
        for j in range(w):
            if pic1[i, j, 0] == pic2[i, j, 0]:  # 像素值 不变,该点 置 黑色
                diff_pic[i, j, :] = 0
            elif pic1[i, j, 0] < pic2[i, j, 0]:  # 像素值 增加,该点 置 绿色
                diff_pic[i, j, 1] = 255
            elif pic1[i, j, 0] > pic2[i, j, 0]:  # 像素值 减少,该点 置 红色
                diff_pic[i, j, 2] = 255
            else:
                logging.error('({},{}): pic1={}, pic2={}'.format(i, j, pic1[i, j, 0], pic2[i, j, 0]))

    cv2.imwrite(dst_path, diff_pic)
    return diff_pic

# 指定轮数 进行 循环 存储和读取
def save_and_read_cycle(pic, path, num):
    for _ in xrange(num):
        cv2.imwrite(path, pic)
        pic = cv2.imread(path)
    return pic

# 原图像
origin_pic = cv2.imread('./data/girl.jpg')

# 需要 循环 1次 的图像
first_pic = origin_pic.copy()
first_pic = save_and_read_cycle(first_pic, first_path, 1)

# 需要 循环 2次 的图像
second_pic = origin_pic.copy()
second_pic = save_and_read_cycle(second_pic, second_path, 2)

# 需要 循环 3次 的图像
third_pic = origin_pic.copy()
third_pic = save_and_read_cycle(third_pic, third_path, 3)

# 需要 循环 4次 的图像
forth_pic = origin_pic.copy()
forth_pic = save_and_read_cycle(forth_pic, forth_path, 4)

# 需要 循环 5次 的图像
fifth_pic = origin_pic.copy()
fifth_pic = save_and_read_cycle(fifth_pic, fifth_path, 5)

# 需要 循环 100次 的图像
one_hundred_pic = origin_pic.copy()
one_hundred_pic = save_and_read_cycle(one_hundred_pic, one_hundred_path, 100)

# 求 循环 存储和读取 后 该图片 与 原图像 差异
ori_fir = compare(origin_pic, first_pic, 'data/ori_fir.jpg')
fir_sec = compare(first_pic, second_pic, 'data/fir_sec.jpg')
sec_thi = compare(second_pic, third_pic, 'data/sec_thi.jpg')
thi_for = compare(third_pic, forth_pic, 'data/thi_for.jpg')
for_fif = compare(forth_pic, fifth_pic, 'data/for_fif.jpg')
ori_hundred = compare(origin_pic, one_hundred_pic, 'data/ori_hundred.jpg')

实验结论

  1. 经过 多轮次 的 循环 后,在 肉眼层面 , 新图像 较 原图像 没有明显的失真
  2. 每一轮 循环存储-读写 后,图片上 各像素点 的 像素值 发生的 变化 会越来越少;
  3. .jpg 是有损压缩格式。

实验不足与展望

不足之处

没有进一步探究压缩算法的原理

没有实验出像素值的偏移区间范围

没有探究循环读写的失真率变化原因

没有制作循环读写的失真率变化曲线图

缺少其他图片进行对比试验,验证实验结论的泛化性

没有在单通道灰度图像上做实验



本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017年10月28日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 起因
    • 遇到问题
      • 修改代码
        • 查找资料
        • 实验
          • 实验思路
            • 实验效果
              • 实验代码
                • 实验结论
                  • 实验不足与展望
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档