├── hupu ├── score.json └── images │ └── 73.jpg ├── SCUT ├── label.txt └── images │ └── AF1684.jpg ├── process.jpg ├── face ├── hupu │ └── 73.jpg └── SCUT │ └── AF1684.jpg ├── README.md ├── face.py ├── .gitignore ├── crawl.py └── keras.py /hupu/score.json: -------------------------------------------------------------------------------- 1 | {"73": 6.74} -------------------------------------------------------------------------------- /SCUT/label.txt: -------------------------------------------------------------------------------- 1 | AF1684.jpg 4.433333 -------------------------------------------------------------------------------- /process.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antct/hupu-score/HEAD/process.jpg -------------------------------------------------------------------------------- /face/hupu/73.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antct/hupu-score/HEAD/face/hupu/73.jpg -------------------------------------------------------------------------------- /hupu/images/73.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antct/hupu-score/HEAD/hupu/images/73.jpg -------------------------------------------------------------------------------- /face/SCUT/AF1684.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antct/hupu-score/HEAD/face/SCUT/AF1684.jpg -------------------------------------------------------------------------------- /SCUT/images/AF1684.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antct/hupu-score/HEAD/SCUT/images/AF1684.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hupu-score 2 | 3 | > HUPU auto-score network based on ResNet-50 4 | 5 | ## Dataset 6 | 7 | Hupu: [盒宫大会api](https://api-staging.jfbapp.cn/quiz/5) 8 | 9 | ```bash 10 | python crawl.py 11 | ``` 12 | 13 | SCUT: [SCUT-FBP5500-Database](https://github.com/HCIILAB/SCUT-FBP5500-Database-Release) 14 | 15 | ## Model 16 | 17 | Network: ResNet50 + FC 18 | 19 | Loss: MAE (MAE >> MSE) 20 | 21 | ## Env 22 | 23 | ```bash 24 | pip install requests 25 | pip install tensorflow-gpu==1.11 26 | pip install face_recognition 27 | ``` 28 | 29 | ## Train 30 | 31 | Fine-Tune: Fix ResNet & Train FC 32 | 33 | ```bash 34 | python keras.py --mode fine_tune 35 | ``` 36 | 37 | Full-Train: Train ResNet & FC 38 | 39 | ```bash 40 | python keras.py --mode train 41 | ``` 42 | 43 | ## Predict 44 | 45 | ```bash 46 | python keras.py --mode predict --image test.jpg 47 | ``` 48 | -------------------------------------------------------------------------------- /face.py: -------------------------------------------------------------------------------- 1 | import face_recognition 2 | import os 3 | from PIL import Image 4 | 5 | 6 | def get_hupu_face(): 7 | if not os.path.exists('./face/hupu/'): 8 | os.makedirs('./face/hupu/') 9 | for i in os.listdir('./hupu/images'): 10 | if not str(i).endswith('jpg'): 11 | continue 12 | print("./hupu/images/{}".format(i)) 13 | image = face_recognition.load_image_file("./hupu/images/{}".format(i)) 14 | try: 15 | face_locations = face_recognition.face_locations(image) 16 | top, right, bottom, left = face_locations[0] 17 | face_image = image[top:bottom, left:right] 18 | pil_image = Image.fromarray(face_image) 19 | pil_image.save('./face/hupu/{}'.format(i)) 20 | except: 21 | continue 22 | 23 | def get_SCUT_face(): 24 | if not os.path.exists('./face/SCUT/'): 25 | os.makedirs('./face/SCUT/') 26 | for i in os.listdir('./SCUT/images'): 27 | if not str(i).endswith('jpg'): 28 | continue 29 | if str(i)[:2] != 'AF': 30 | continue 31 | 32 | print("./SCUT/images/{}".format(i)) 33 | image = face_recognition.load_image_file("./SCUT/images/{}".format(i)) 34 | try: 35 | face_locations = face_recognition.face_locations(image) 36 | top, right, bottom, left = face_locations[0] 37 | face_image = image[top:bottom, left:right] 38 | pil_image = Image.fromarray(face_image) 39 | pil_image.save('./face/SCUT/{}'.format(i)) 40 | except Exception as e: 41 | continue 42 | 43 | get_SCUT_face() 44 | get_hupu_face() -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | # models 107 | models/ 108 | -------------------------------------------------------------------------------- /crawl.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import os 3 | 4 | class crawler(): 5 | def __init__(self): 6 | self.url = 'https://api-staging.jfbapp.cn/quiz/' 7 | self.path = './hupu' 8 | self.image_path = './hupu/images' 9 | if not os.path.exists(self.image_path): 10 | os.makedirs(self.image_path) 11 | self.headers = {'User-Agent': 'Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)'} 12 | 13 | def go(self, begin=5, end=122): 14 | score = {} 15 | for i in range(begin, end): 16 | try: 17 | res = requests.get(self.url + str(i), headers=self.headers).json() 18 | print("get {}".format(res['quiz']['title'])) 19 | questions = res['questions'] 20 | for question in questions: 21 | image_id = question['id'] 22 | image_url = question['image'] 23 | avg_score = question['avgScore'] 24 | score[image_id] = avg_score 25 | for _ in range(2): 26 | try: 27 | image_name = '{}/images/{}.jpg'.format(self.path, image_id) 28 | image_res = requests.get(image_url) 29 | with open(image_name, "wb") as f: 30 | f.write(image_res.content) 31 | break 32 | except Exception as e: 33 | print(e) 34 | except Exception as e: 35 | print(e) 36 | continue 37 | with open('{}/{}'.format(self.path, 'score.json'), 'w') as f: 38 | f.write(str(score).replace("'", '"')) 39 | 40 | if __name__ == "__main__": 41 | obj = crawler() 42 | obj.go() 43 | -------------------------------------------------------------------------------- /keras.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | from tensorflow.keras.applications.resnet50 import ResNet50 3 | from tensorflow.keras.models import Model, Sequential 4 | from tensorflow.keras.layers import Dense, Flatten, Dropout 5 | import numpy as np 6 | 7 | from tensorflow.keras import backend as K 8 | from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping 9 | from PIL import Image 10 | from tensorflow.keras.optimizers import Adam 11 | 12 | import os 13 | os.environ['CUDA_VISIBLE_DEVICES']='0' 14 | 15 | import argparse 16 | parser = argparse.ArgumentParser() 17 | 18 | parser.add_argument('--mode', dest='mode', default='fine_tune') 19 | parser.add_argument('--input_size', dest='input_size', default=256) 20 | parser.add_argument('--batch_size', dest='batch_size', default=32) 21 | parser.add_argument('--fix_layers', dest='fix_layers', default=0) 22 | parser.add_argument('--max_epochs', dest='max_epochs', default=100) 23 | parser.add_argument('--patience', dest='patience', default=8) 24 | parser.add_argument('--image', dest='image') 25 | args = parser.parse_args() 26 | 27 | 28 | def r_square(y_true, y_pred): 29 | SSR = K.sum(K.square(y_pred-y_true), axis=-1) 30 | SST = K.sum(K.square(y_true-K.mean(y_true)), axis=-1) 31 | return 1 - SSR/(SST+1e-6) 32 | 33 | 34 | class model(): 35 | def __init__(self, args): 36 | self.model = None 37 | self.args = args 38 | 39 | 40 | def _build_SCUT_train_data(self): 41 | data = [] 42 | with open('./SCUT/label.txt', 'r') as f: 43 | data = f.readlines() 44 | data = [i.strip('\n').split(' ') for i in data] 45 | images = [] 46 | labels = [] 47 | for i in data: 48 | if i[0][:2] != 'AF': 49 | continue 50 | try: 51 | fname = './face/SCUT/{}'.format(i[0]) 52 | images.append(np.array(Image.open(fname).convert('RGB').resize((self.args.input_size, self.args.input_size), Image.LANCZOS)) / 255.0) 53 | labels.append(float(i[1])*2-1) 54 | except: 55 | continue 56 | 57 | images = np.array(images) 58 | labels = np.array(labels) 59 | return images, labels 60 | 61 | def _build_hupu_train_data(self): 62 | scores = {} 63 | with open('./hupu/score.json', 'r') as f: 64 | scores = dict(eval(f.read())) 65 | data = [] 66 | for i in os.listdir('./face/hupu'): 67 | if str(i).endswith('jpg'): 68 | data.append(i) 69 | 70 | images = [] 71 | labels = [] 72 | for i in data: 73 | fname = './face/hupu/{}'.format(i) 74 | images.append(np.array(Image.open(fname).convert('RGB').resize((self.args.input_size, self.args.input_size), Image.LANCZOS)) / 255.0) 75 | score = float(scores[i[:-4]]) 76 | labels.append(score) 77 | 78 | avg_score = sum(labels)/len(labels) 79 | labels = [avg_score + (i - avg_score) * 1.25 for i in labels] 80 | 81 | images = images 82 | labels = labels 83 | 84 | images = np.array(images) 85 | labels = np.array(labels) 86 | return images, labels 87 | 88 | def _build_train_data(self): 89 | hupu_images, hupu_labels = self._build_hupu_train_data() 90 | SCUT_images, SCUT_labels = self._build_SCUT_train_data() 91 | images = np.concatenate((hupu_images, SCUT_images), axis=0) 92 | labels = np.concatenate((hupu_labels, SCUT_labels), axis=0) 93 | return images, labels 94 | 95 | def _build_graph(self): 96 | resnet = ResNet50(include_top=False, pooling='avg', input_shape=(self.args.input_size, self.args.input_size, 3), weights='imagenet') 97 | self.model = Sequential() 98 | self.model.add(resnet) 99 | self.model.add(Dense(1)) 100 | if self.mode == 'fine_tune': 101 | self.model.layers[0].trainable = False 102 | print(self.model.summary()) 103 | if self.mode == 'train': 104 | # for i in self.model.layers[:-1 * self.args.fix_layers]: 105 | # i.trainable = False 106 | print(self.model.summary()) 107 | self.model.load_weights('fine_tune_best.h5') 108 | if self.mode == 'predict': 109 | self.model.load_weights('train_best.h5') 110 | self.model.compile(loss='mae', optimizer='Adam', metrics=[r_square]) 111 | 112 | 113 | def fine_tune(self): 114 | self.mode = 'fine_tune' 115 | self._build_graph() 116 | x, y = self._build_train_data() 117 | model_ckpt = ModelCheckpoint(filepath='fine_tune_best.h5', save_best_only=True, mode='min', monitor='val_loss', verbose=1) 118 | early_stop = EarlyStopping( monitor='val_loss', patience=self.args.patience, verbose=1, mode='min') 119 | self.model.fit(x=x, 120 | y=y, 121 | batch_size=self.args.batch_size, 122 | epochs=self.args.max_epochs, 123 | verbose=1, 124 | callbacks=[model_ckpt, early_stop], 125 | validation_split=0.2, 126 | shuffle=True) 127 | 128 | 129 | def train(self): 130 | self.mode = 'train' 131 | self._build_graph() 132 | x, y = self._build_train_data() 133 | model_ckpt = ModelCheckpoint(filepath='train_best.h5', save_best_only=True, mode='min', monitor='val_loss', verbose=1) 134 | early_stop = EarlyStopping( monitor='val_loss', patience=self.args.patience, verbose=1, mode='min') 135 | self.model.fit(x=x, 136 | y=y, 137 | batch_size=self.args.batch_size, 138 | epochs=self.args.max_epochs, 139 | verbose=1, 140 | callbacks=[model_ckpt, early_stop], 141 | validation_split=0.2, 142 | shuffle=True) 143 | 144 | def predict(self, fname): 145 | import face_recognition 146 | self.mode = 'predict' 147 | self._build_graph() 148 | images = [] 149 | 150 | image = face_recognition.load_image_file(fname) 151 | try: 152 | face_locations = face_recognition.face_locations(image) 153 | top, right, bottom, left = face_locations[0] 154 | face_image = image[top:bottom, left:right] 155 | image = Image.fromarray(face_image) 156 | images.append(np.array(image.convert('RGB').resize((self.args.input_size, self.args.input_size), Image.LANCZOS)) / 255.0) 157 | except Exception as e: 158 | pass 159 | 160 | images = np.array(images) 161 | print(self.model.predict(images)) 162 | 163 | if __name__ == '__main__': 164 | tf.device('/gpu:0') 165 | model = model(args) 166 | if args.mode == 'train': 167 | model.train() 168 | if args.mode == 'fine_tune': 169 | model.fine_tune() 170 | if args.mode == 'predict': 171 | model.predict(args.image) 172 | --------------------------------------------------------------------------------