# -*- coding: utf-8 -*-
import argparse
import json
import operator
import os
import random
from keras.callbacks import EarlyStopping
from skimage import exposure
import cv2
import imageio
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import tensorflow as tf
from keras import backend as K
from keras.layers import Flatten, BatchNormalization
from keras.layers import Input
from keras.layers import concatenate
from keras.layers.convolutional import AveragePooling2D
from keras.layers.convolutional import Conv2D
from keras.layers.convolutional import MaxPooling2D
from keras.layers.core import Activation
from keras.layers.core import Dense
from keras.layers.core import Dropout
from keras.models import Model
from keras.preprocessing.image import ImageDataGenerator
from keras.preprocessing.image import img_to_array
from keras.utils.np_utils import to_categorical
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
gpus = tf.config.list_physical_devices(device_type='GPU')
for gpu in gpus:
tf.config.experimental.set_memory_growth(device=gpu, enable=True)
src = "data\\"
model_src = "model\\"
photo_dir = r'photo/'
if not os.path.exists(src):
os.makedirs(src)
if not os.path.exists(model_src):
os.makedirs(model_src)
if not os.path.exists(photo_dir):
os.makedirs(photo_dir)
# 参数配置
ap = argparse.ArgumentParser()
# 输出文件夹路径
ap.add_argument("-m", "--model", type=str, default=model_src,
help="path to output model")
# 输出loss的迭代图片
ap.add_argument("-p", "--plot", type=str, default="plot.png",
help="path to output loss/accuracy plot")
args = vars(ap.parse_args())
def gasuss_noise(image, mean=0, var=0.001):
'''
手动添加高斯噪声
mean : 均值
var : 方差
'''
image = np.array(image / 255, dtype=float)
noise = np.random.normal(mean, var ** 0.5, image.shape) # 正态分布
out = image + noise
if out.min() < 0:
low_clip = -1.
else:
low_clip = 0.
out = np.clip(out, low_clip, 1.0)
out = np.uint8(out * 255)
return out
def sp_noise(image, prob):
'''
手动添加椒盐噪声
prob:噪声比例
'''
output = np.zeros(image.shape, np.uint8)
thres = 1 - prob
for i in range(image.shape[0]):
for j in range(image.shape[1]):
rdn = random.random()
if rdn < prob:
output[i][j] = 0
elif rdn > thres:
output[i][j] = 255
else:
output[i][j] = image[i][j]
return output
def trans(img): # 转换颜色
img2 = img
Size = img.shape
width = Size[0]
length = Size[1]
list1_red = [0, 0, 128]
list2_green = [0, 128, 0]
for i in range(0, width):
for j in range(0, length):
if operator.eq(img[i][j].tolist(), list1_red) == True:
img2[i][j] = np.array([0, 128, 0])
elif operator.eq(img[i][j].tolist(), list2_green) == True:
img2[i][j] = np.array([0, 0, 128])
return img2
def readTxt(filepath):
try:
with open(filepath, 'r', encoding='gbk') as f:
lines = []
for one in f:
one = one.rstrip("\n\t")
lines.append(one)
f.close()
return lines
except:
with open(filepath, 'r', encoding='utf-8') as f:
lines = []
for one in f:
one = one.rstrip("\n\t")
lines.append(one)
f.close()
return lines
# 写Json文件,写一行就换行,追加方式
def writeJson(relate_record, src):
Json_str = json.dumps(relate_record, ensure_ascii=False)
with open(src, 'w') as Json_file:
Json_file.write(Json_str)
Json_file.close()
class MiniGoogLeNet:
@staticmethod
def conv_module(x, K, kX, kY, stride, chanDim, padding="same"): # x表示输入数据,K表示conv的filter的数量,KX,KY表示kernel_size
# define a CONV => BN => RELU pattern,我们严格按照原论文的说法,使用CONV => BN => RELU的顺序,但是实际上,CONV => Relu => BN的效果会更好一些
x = Conv2D(K, (kX, kY), strides=stride, padding=padding)(x)
x = BatchNormalization(axis=chanDim)(x)
x = Activation("relu")(x)
# return the block
return x
@staticmethod
def inception_module(x, numK1_1, numK3_3,
chanDim): # x表示输入数据,numK1_1,numK3_3表示kernel的filter的数量,chanDim:first_channel or last_channel
conv1_1 = MiniGoogLeNet.conv_module(x, numK1_1, 1, 1, (1, 1), chanDim)
conv3_3 = MiniGoogLeNet.conv_module(x, numK3_3, 3, 3, (1, 1), chanDim)
x = concatenate([conv1_1, conv3_3], axis=chanDim) # 将conv1_1和conv3_3串联到一起
return x
@staticmethod
def downsample_module(x, K, chanDim): # K表示conv的filter的数量
conv3_3 = MiniGoogLeNet.conv_module(x, K, 3, 3, (2, 2), chanDim,
padding='valid') # padding=same表示:出输入和输出的size是相同的,由于加入了padding,如果是padding=valid,那么padding=0
pool = MaxPooling2D((3, 3), strides=(2, 2))(x)
x = concatenate([conv3_3, pool], axis=chanDim) # 将conv3_3和maxPooling串到一起
return x
@staticmethod
def build(width, height, depth, classes):
inputShape = (height, width, depth) # keras默认channel last,tf作为backend
chanDim = -1
# if we are using "channels first", update the input shape
# and channels dimension
if K.image_data_format() == "channels_first":
inputShape = (depth, height, width)
chanDim = 1
# define the model input and first CONV module
inputs = Input(shape=inputShape)
x = MiniGoogLeNet.conv_module(inputs, 96, 3, 3, (1, 1), chanDim)
# two Inception modules followed by a downsample module
x = MiniGoogLeNet.inception_module(x, 32, 32, chanDim) # 第一个分叉
x = MiniGoogLeNet.inception_module(x, 32, 48, chanDim) # 第二个分叉
x = MiniGoogLeNet.downsample_module(x, 80, chanDim) # 第三个分叉,含有maxpooling
# four Inception modules followed by a downsample module
x = MiniGoogLeNet.inception_module(x, 112, 48, chanDim)
x = MiniGoogLeNet.inception_module(x, 96, 64, chanDim)
x = MiniGoogLeNet.inception_module(x, 80, 80, chanDim)
x = MiniGoogLeNet.inception_module(x, 48, 96, chanDim)
x = MiniGoogLeNet.downsample_module(x, 96, chanDim)
# two Inception modules followed by global POOL and dropout
x = MiniGoogLeNet.inception_module(x, 176, 160, chanDim)
x = MiniGoogLeNet.inception_module(x, 176, 160, chanDim) # 输出是(7×7×(160+176))
x = AveragePooling2D((7, 7))(x) # 经过平均池化之后变成了(1*1*376)
x = Dropout(0.5)(x)
# softmax classifier
x = Flatten()(x) # 特征扁平化
x = Dense(classes)(x) # 全连接层,进行多分类,形成最终的10分类
x = Activation("softmax")(x)
# create the model
model = Model(inputs, x, name="googlenet")
# return the constructed network architecture
return model
def build_train(path):
files = os.listdir(path)
count = len(files)
for imagePath in files:
try:
image = imageio.imread(path + '/' + imagePath)
except:
continue
image = cv2.resize(image, (image_shape, image_shape))
# 加噪音
out3 = gasuss_noise(image)
out4 = sp_noise(image,