经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Python » 查看文章
图片去摩尔纹简述实现python代码示例
来源:jb51  时间:2023/2/27 9:41:53  对本文有异议

1、前言

当感光元件像素的空间频率与影像中条纹的空间频率接近时,可能产生一种新的波浪形的干扰图案,即所谓的摩尔纹。传感器的网格状纹理构成了一个这样的图案。当图案中的细条状结构与传感器的结构以小角度交叉时,这种效应也会在图像中产生明显的干扰。这种现象在一些细密纹理情况下,比如时尚摄影中的布料上,非常普遍。这种摩尔纹可能通过亮度也可能通过颜色来展现。但是在这里,仅针对在翻拍过程中产生的图像摩尔纹进行处理。

翻拍即从计算机屏幕上捕获图片,或对着屏幕拍摄图片;该方式会在图片上产生摩尔纹现象

论文主要处理思路

  • 对原图作Haar变换得到四个下采样特征图(原图下二采样cA、Horizontal横向高频cH、Vertical纵向高频cV、Diagonal斜向高频cD)
  • 然后分别利用四个独立的CNN对四个下采样特征图卷积池化,提取特征信息
  • 原文随后对三个高频信息卷积池化后的结果的每个channel、每个像素点比对,取max
  • 将上一步得到的结果和cA卷积池化后的结果作笛卡尔积

论文地址

2、网络结构复现

  如下图所示,本项目复现了论文的图像去摩尔纹方法,并对数据处理部分进行了修改,并且网络结构上也参考了源码中的结构,对图片产生四个下采样特征图,而不是论文中的三个,具体处理方式大家可以参考一下网络结构。

  1. import math
  2. import paddle
  3. import paddle.nn as nn
  4. import paddle.nn.functional as F
  5. # import pywt
  6. from paddle.nn import Linear, Dropout, ReLU
  7. from paddle.nn import Conv2D, MaxPool2D
  8. class mcnn(nn.Layer):
  9. def __init__(self, num_classes=1000):
  10. super(mcnn, self).__init__()
  11. self.num_classes = num_classes
  12. self._conv1_LL = Conv2D(3,32,7,stride=2,padding=1,)
  13. # self.bn1_LL = nn.BatchNorm2D(128)
  14. self._conv1_LH = Conv2D(3,32,7,stride=2,padding=1,)
  15. # self.bn1_LH = nn.BatchNorm2D(256)
  16. self._conv1_HL = Conv2D(3,32,7,stride=2,padding=1,)
  17. # self.bn1_HL = nn.BatchNorm2D(512)
  18. self._conv1_HH = Conv2D(3,32,7,stride=2,padding=1,)
  19. # self.bn1_HH = nn.BatchNorm2D(256)
  20. self.pool_1_LL = nn.MaxPool2D(kernel_size=2,stride=2, padding=0)
  21. self.pool_1_LH = nn.MaxPool2D(kernel_size=2,stride=2, padding=0)
  22. self.pool_1_HL = nn.MaxPool2D(kernel_size=2,stride=2, padding=0)
  23. self.pool_1_HH = nn.MaxPool2D(kernel_size=2,stride=2, padding=0)
  24. self._conv2 = Conv2D(32,16,3,stride=2,padding=1,)
  25. self.pool_2 = nn.MaxPool2D(kernel_size=2,stride=2, padding=0)
  26. self.dropout2 = Dropout(p=0.5)
  27. self._conv3 = Conv2D(16,32,3,stride=2,padding=1,)
  28. self.pool_3 = nn.MaxPool2D(kernel_size=2,stride=2, padding=0)
  29. self._conv4 = Conv2D(32,32,3,stride=2,padding=1,)
  30. self.pool_4 = nn.MaxPool2D(kernel_size=2,stride=2, padding=0)
  31. self.dropout4 = Dropout(p=0.5)
  32. # self.bn1_HH = nn.BatchNorm1D(256)
  33. self._fc1 = Linear(in_features=64,out_features=num_classes)
  34. self.dropout5 = Dropout(p=0.5)
  35. self._fc2 = Linear(in_features=2,out_features=num_classes)
  36. def forward(self, inputs1, inputs2, inputs3, inputs4):
  37. x1_LL = self._conv1_LL(inputs1)
  38. x1_LL = F.relu(x1_LL)
  39. x1_LH = self._conv1_LH(inputs2)
  40. x1_LH = F.relu(x1_LH)
  41. x1_HL = self._conv1_HL(inputs3)
  42. x1_HL = F.relu(x1_HL)
  43. x1_HH = self._conv1_HH(inputs4)
  44. x1_HH = F.relu(x1_HH)
  45. pool_x1_LL = self.pool_1_LL(x1_LL)
  46. pool_x1_LH = self.pool_1_LH(x1_LH)
  47. pool_x1_HL = self.pool_1_HL(x1_HL)
  48. pool_x1_HH = self.pool_1_HH(x1_HH)
  49. temp = paddle.maximum(pool_x1_LH, pool_x1_HL)
  50. avg_LH_HL_HH = paddle.maximum(temp, pool_x1_HH)
  51. inp_merged = paddle.multiply(pool_x1_LL, avg_LH_HL_HH)
  52. x2 = self._conv2(inp_merged)
  53. x2 = F.relu(x2)
  54. x2 = self.pool_2(x2)
  55. x2 = self.dropout2(x2)
  56. x3 = self._conv3(x2)
  57. x3 = F.relu(x3)
  58. x3 = self.pool_3(x3)
  59. x4 = self._conv4(x3)
  60. x4 = F.relu(x4)
  61. x4 = self.pool_4(x4)
  62. x4 = self.dropout4(x4)
  63. x4 = paddle.flatten(x4, start_axis=1, stop_axis=-1)
  64. x5 = self._fc1(x4)
  65. x5 = self.dropout5(x5)
  66. out = self._fc2(x5)
  67. return out
  68. model_res = mcnn(num_classes=2)
  69. paddle.summary(model_res,[(1,3,512,384),(1,3,512,384),(1,3,512,384),(1,3,512,384)])
  1. ---------------------------------------------------------------------------
  2. Layer (type) Input Shape Output Shape Param #
  3. ===========================================================================
  4. Conv2D-1 [[1, 3, 512, 384]] [1, 32, 254, 190] 4,736
  5. Conv2D-2 [[1, 3, 512, 384]] [1, 32, 254, 190] 4,736
  6. Conv2D-3 [[1, 3, 512, 384]] [1, 32, 254, 190] 4,736
  7. Conv2D-4 [[1, 3, 512, 384]] [1, 32, 254, 190] 4,736
  8. MaxPool2D-1 [[1, 32, 254, 190]] [1, 32, 127, 95] 0
  9. MaxPool2D-2 [[1, 32, 254, 190]] [1, 32, 127, 95] 0
  10. MaxPool2D-3 [[1, 32, 254, 190]] [1, 32, 127, 95] 0
  11. MaxPool2D-4 [[1, 32, 254, 190]] [1, 32, 127, 95] 0
  12. Conv2D-5 [[1, 32, 127, 95]] [1, 16, 64, 48] 4,624
  13. MaxPool2D-5 [[1, 16, 64, 48]] [1, 16, 32, 24] 0
  14. Dropout-1 [[1, 16, 32, 24]] [1, 16, 32, 24] 0
  15. Conv2D-6 [[1, 16, 32, 24]] [1, 32, 16, 12] 4,640
  16. MaxPool2D-6 [[1, 32, 16, 12]] [1, 32, 8, 6] 0
  17. Conv2D-7 [[1, 32, 8, 6]] [1, 32, 4, 3] 9,248
  18. MaxPool2D-7 [[1, 32, 4, 3]] [1, 32, 2, 1] 0
  19. Dropout-2 [[1, 32, 2, 1]] [1, 32, 2, 1] 0
  20. Linear-1 [[1, 64]] [1, 2] 130
  21. Dropout-3 [[1, 2]] [1, 2] 0
  22. Linear-2 [[1, 2]] [1, 2] 6
  23. ===========================================================================
  24. Total params: 37,592
  25. Trainable params: 37,592
  26. Non-trainable params: 0
  27. ---------------------------------------------------------------------------
  28. Input size (MB): 9.00
  29. Forward/backward pass size (MB): 59.54
  30. Params size (MB): 0.14
  31. Estimated Total Size (MB): 68.68
  32. ---------------------------------------------------------------------------
  33. {'total_params': 37592, 'trainable_params': 37592}

3、数据预处理

  与源代码不同的是,本项目将图像的小波分解部分集成在了数据读取部分,即改为了线上进行小波分解,而不是源代码中的线下进行小波分解并且保存图片。首先,定义小波分解的函数

  1. !pip install PyWavelets
  1. import numpy as np
  2. import pywt
  3. def splitFreqBands(img, levRows, levCols):
  4. halfRow = int(levRows/2)
  5. halfCol = int(levCols/2)
  6. LL = img[0:halfRow, 0:halfCol]
  7. LH = img[0:halfRow, halfCol:levCols]
  8. HL = img[halfRow:levRows, 0:halfCol]
  9. HH = img[halfRow:levRows, halfCol:levCols]
  10. return LL, LH, HL, HH
  11. def haarDWT1D(data, length):
  12. avg0 = 0.5;
  13. avg1 = 0.5;
  14. dif0 = 0.5;
  15. dif1 = -0.5;
  16. temp = np.empty_like(data)
  17. # temp = temp.astype(float)
  18. temp = temp.astype(np.uint8)
  19. h = int(length/2)
  20. for i in range(h):
  21. k = i*2
  22. temp[i] = data[k] * avg0 + data[k + 1] * avg1;
  23. temp[i + h] = data[k] * dif0 + data[k + 1] * dif1;
  24. data[:] = temp
  25. # computes the homography coefficients for PIL.Image.transform using point correspondences
  26. def fwdHaarDWT2D(img):
  27. img = np.array(img)
  28. levRows = img.shape[0];
  29. levCols = img.shape[1];
  30. # img = img.astype(float)
  31. img = img.astype(np.uint8)
  32. for i in range(levRows):
  33. row = img[i,:]
  34. haarDWT1D(row, levCols)
  35. img[i,:] = row
  36. for j in range(levCols):
  37. col = img[:,j]
  38. haarDWT1D(col, levRows)
  39. img[:,j] = col
  40. return splitFreqBands(img, levRows, levCols)
  1. !cd "data/data188843/" && unzip -q 'total_images.zip'
  1. import os
  2. recapture_keys = [ 'ValidationMoire']
  3. original_keys = ['ValidationClear']
  4. def get_image_label_from_folder_name(folder_name):
  5. """
  6. :param folder_name:
  7. :return:
  8. """
  9. for key in original_keys:
  10. if key in folder_name:
  11. return 'original'
  12. for key in recapture_keys:
  13. if key in folder_name:
  14. return 'recapture'
  15. return 'unclear'
  16. label_name2label_id = {
  17. 'original': 0,
  18. 'recapture': 1,}
  19. src_image_dir = "data/data188843/total_images"
  20. dst_file = "data/data188843/total_images/train.txt"
  21. image_folder = [file for file in os.listdir(src_image_dir)]
  22. print(image_folder)
  23. image_anno_list = []
  24. for folder in image_folder:
  25. label_name = get_image_label_from_folder_name(folder)
  26. # label_id = label_name2label_id.get(label_name, 0)
  27. label_id = label_name2label_id[label_name]
  28. folder_path = os.path.join(src_image_dir, folder)
  29. image_file_list = [file for file in os.listdir(folder_path) if
  30. file.endswith('.jpg') or file.endswith('.jpeg') or
  31. file.endswith('.JPG') or file.endswith('.JPEG') or file.endswith('.png')]
  32. for image_file in image_file_list:
  33. # if need_root_dir:
  34. # image_path = os.path.join(folder_path, image_file)
  35. # else:
  36. image_path = image_file
  37. image_anno_list.append(folder +"/"+image_path +"\t"+ str(label_id) + '\n')
  38. dst_path = os.path.dirname(src_image_dir)
  39. if not os.path.exists(dst_path):
  40. os.makedirs(dst_path)
  41. with open(dst_file, 'w') as fd:
  42. fd.writelines(image_anno_list)
  1. import paddle
  2. import numpy as np
  3. import pandas as pd
  4. import PIL.Image as Image
  5. from paddle.vision import transforms
  6. # from haar2D import fwdHaarDWT2D
  7. paddle.disable_static()
  8. # 定义数据预处理
  9. data_transforms = transforms.Compose([
  10. transforms.Resize(size=(448,448)),
  11. transforms.ToTensor(), # transpose操作 + (img / 255)
  12. # transforms.Normalize( # 减均值 除标准差
  13. # mean=[0.31169346, 0.25506335, 0.12432463],
  14. # std=[0.34042713, 0.29819837, 0.1375536])
  15. #计算过程:output[channel] = (input[channel] - mean[channel]) / std[channel]
  16. ])
  17. # 构建Dataset
  18. class MyDataset(paddle.io.Dataset):
  19. """
  20. 步骤一:继承paddle.io.Dataset类
  21. """
  22. def __init__(self, train_img_list, val_img_list, train_label_list, val_label_list, mode='train', ):
  23. """
  24. 步骤二:实现构造函数,定义数据读取方式,划分训练和测试数据集
  25. """
  26. super(MyDataset, self).__init__()
  27. self.img = []
  28. self.label = []
  29. # 借助pandas读csv的库
  30. self.train_images = train_img_list
  31. self.test_images = val_img_list
  32. self.train_label = train_label_list
  33. self.test_label = val_label_list
  34. if mode == 'train':
  35. # 读train_images的数据
  36. for img,la in zip(self.train_images, self.train_label):
  37. self.img.append('/home/aistudio/data/data188843/total_images/'+img)
  38. self.label.append(paddle.to_tensor(int(la), dtype='int64'))
  39. else:
  40. # 读test_images的数据
  41. for img,la in zip(self.test_images, self.test_label):
  42. self.img.append('/home/aistudio/data/data188843/total_images/'+img)
  43. self.label.append(paddle.to_tensor(int(la), dtype='int64'))
  44. def load_img(self, image_path):
  45. # 实际使用时使用Pillow相关库进行图片读取即可,这里我们对数据先做个模拟
  46. image = Image.open(image_path).convert('RGB')
  47. # image = data_transforms(image)
  48. return image
  49. def __getitem__(self, index):
  50. """
  51. 步骤三:实现__getitem__方法,定义指定index时如何获取数据,并返回单条数据(训练数据,对应的标签)
  52. """
  53. image = self.load_img(self.img[index])
  54. LL, LH, HL, HH = fwdHaarDWT2D(image)
  55. label = self.label[index]
  56. # print(LL.shape)
  57. # print(LH.shape)
  58. # print(HL.shape)
  59. # print(HH.shape)
  60. LL = data_transforms(LL)
  61. LH = data_transforms(LH)
  62. HL = data_transforms(HL)
  63. HH = data_transforms(HH)
  64. print(type(LL))
  65. print(LL.dtype)
  66. return LL, LH, HL, HH, np.array(label, dtype='int64')
  67. def __len__(self):
  68. """
  69. 步骤四:实现__len__方法,返回数据集总数目
  70. """
  71. return len(self.img)
  72. image_file_txt = '/home/aistudio/data/data188843/total_images/train.txt'
  73. with open(image_file_txt) as fd:
  74. lines = fd.readlines()
  75. train_img_list = list()
  76. train_label_list = list()
  77. for line in lines:
  78. split_list = line.strip().split()
  79. image_name, label_id = split_list
  80. train_img_list.append(image_name)
  81. train_label_list.append(label_id)
  82. # print(train_img_list)
  83. # print(train_label_list)
  84. # 测试定义的数据集
  85. train_dataset = MyDataset(mode='train',train_label_list=train_label_list, train_img_list=train_img_list, val_img_list=train_img_list, val_label_list=train_label_list)
  86. # test_dataset = MyDataset(mode='test')
  87. # 构建训练集数据加载器
  88. train_loader = paddle.io.DataLoader(train_dataset, batch_size=2, shuffle=True)
  89. # 构建测试集数据加载器
  90. valid_loader = paddle.io.DataLoader(train_dataset, batch_size=2, shuffle=True)
  91. print('=============train dataset=============')
  92. for LL, LH, HL, HH, label in train_dataset:
  93. print('label: {}'.format(label))
  94. break

4、模型训练

  1. model2 = paddle.Model(model_res)
  2. model2.prepare(optimizer=paddle.optimizer.Adam(parameters=model2.parameters()),
  3. loss=nn.CrossEntropyLoss(),
  4. metrics=paddle.metric.Accuracy())
  5. model2.fit(train_loader,
  6. valid_loader,
  7. epochs=5,
  8. verbose=1,
  9. )

总结

本项目主要介绍了如何使用卷积神经网络去检测翻拍图片,主要为摩尔纹图片;其主要创新点在于网络结构上,将图片的高低频信息分开处理。

在本项目中,CNN 仅使用 1 级小波分解进行训练。 可以探索对多级小波分解网络精度的影响。 CNN 模型可以用更多更难的例子和更深的网络进行训练,更多关于python 图片去摩尔纹的资料请关注w3xue其它相关文章!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号