年齢・性別判定アプリ
自己紹介
はじめまして!
私はアプリ開発のPMとして働いております。
普段の業務では主にプロジェクトのWBS・TODOの管理、連携企業様とのスケジュール調整業務を行っております。
Python学習のきっかけ
普段の仕事では、実際にコードは書いておらず、プロジェクトのマネージメント業務を主に行なっております。
前々から自分でもコードを書いてWebサービスを作ってみたく、今最も注目されているAI開発に有利なPythonでの開発を決めました。
やりたいこと
まだまだ学習途中であるため、まずは基礎的な画像認識アプリを作ります。
顔の写真から、予想年齢と性別を判定させるサイトを作ります。
データセット
Caffeのデータセットを使用します。
URL : http://caffe.berkeleyvision.org/
ファイル名は「"age_net.caffemodel"と "gender_net.caffemodel"」です。
年齢と性別判定のためのデータセットです。
実行
準備
root_dir = "./model_files/" # relative - モデル ファイルのルート ディレクトリ
faceProto = root_dir + "opencv_face_detector.pbtxt" #顔検出の設定ファイル
faceModel = root_dir + "opencv_face_detector_uint8.pb" #顔検出用の事前トレーニング済みモデル
ageProto = root_dir + "age_deploy.prototxt" #年齢検出の設定ファイル
ageModel = root_dir + "age_net.caffemodel" #年齢検出のための事前トレーニング済みモデル
genderProto = root_dir + "gender_deploy.prototxt" #性別検出の構成ファイル
genderModel = root_dir + "gender_net.caffemodel" #性別検出のための事前トレーニング済みモデル
MODEL_MEAN_VALUES = (78.4263377603, 87.7689143744, 114.895847746) # 性別検出の平均値
ageList = ['0-2', '4-6', '8-12', '15-20', '25-32', '38-43', '48-53', '60-100']
genderList = ['Male', 'Female']
ageNet = cv2.dnn.readNet(ageModel,ageProto) # 最初のパラメーターはトレーニングの重みを保存するために使用され、2 番目のパラメーターはネットワークを保存するために使用されます
genderNet = cv2.dnn.readNet(genderModel, genderProto) # 最初のパラメーターはトレーニングの重みを保存するために使用され、2 番目のパラメーターはネットワークを保存するために使用されます
faceNet = cv2.dnn.readNet(faceModel, faceProto) # 最初のパラメーターはトレーニングの重みを保存するために使用され、2 番目のパラメーターはネットワークを保存するために使用されます
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = 'static/uploads' #このフォルダには、アップロードされたすべての写真とデフォルトの写真が含まれます
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # ファイルサイズ上限16MB
def resize_image(raw_img):
frame = cv2.imread(raw_img) # 元の画像は、次のコード行で使用するために保存されます
# 画像処理時間を短縮するためにサイズを変更します
small_frame = cv2.resize(frame, (0, 0), fx=0.5, fy=0.5) # fx=0.5, fy=0.5 means the image is being scaled down to half its original size
return small_frame, frame
def getFaceBox(net, frame, conf_threshold = 0.75): #顔検出の信頼度しきい値は、デフォルトで 75% です。
frameOpencvDnn = frame.copy()
frameHeight = frameOpencvDnn.shape[0]
frameWidth = frameOpencvDnn.shape[1]
# 画像処理のためにスケーリングと正規化がここで行われます
# パラメータは次のとおりです: 画像、倍率、スケーリング後の画像のサイズ、画像の平均値、フラグ
blob = cv2.dnn.blobFromImage(frameOpencvDnn,1.0,(300,300),[104, 117, 123], True, False)
net.setInput(blob) #dnn には入力画像が供給されます (スケーリングおよび正規化されたバージョン)
detections = net.forward() # ニューラルネットワークの学習した重みが入力データに適用され、予測が生成されます
bboxes = [] #検出された画像の境界 (4 ポイント) がこのリストに追加されます
for i in range(detections.shape[2]):
confidence = detections[0,0,i,2]
if confidence > conf_threshold:
x1 = int(detections[0,0,i,3]* frameWidth)
y1 = int(detections[0,0,i,4]* frameHeight)
x2 = int(detections[0,0,i,5]* frameWidth)
y2 = int(detections[0,0,i,6]* frameHeight)
bboxes.append([x1,y1,x2,y2])
return frameOpencvDnn , bboxes
imagename = "default.jpg" # ホームページを読み込むときにユーザーに表示されるデフォルトの画像
image_path = os.path.join(app.config['UPLOAD_FOLDER'], imagename) # デフォルトのイメージの正確なパスを知る
@app.route('/', methods=['GET', 'POST'])
実行部分
def upload():
global imagename, image_path
if request.method == 'POST': # この関数は、「アップロード」ボタンがクリックされるとすぐにトリガーされます
if 'file' not in request.files:
return 'No file part in the request'
file = request.files['file'] #ユーザーが提供する画像ファイルを取得する
imagename = file.filename # ここで画像名が更新され、「default.jpg」テキストが置き換えられます
image_path = os.path.join(app.config['UPLOAD_FOLDER'], imagename) # アップロードされたファイルがどこにあるかを知る
if file and file.content_length > app.config['MAX_CONTENT_LENGTH']: # ファイルが大きすぎるかどうかを確認する
return 'File size exceeded the limit of 16 MB' # ファイルが大きすぎる場合、エラーページが返されます
if imagename == '': #画像ファイルが選択されていない場合、名前はデフォルトに戻す
imagename = "default.jpg"
image_path = os.path.join(app.config['UPLOAD_FOLDER'], imagename)
else:
file.save(image_path) # 「アップロード」フォルダに画像を保存する
return render_template('index.html', image_path=image_path, imagename=imagename)
@app.route('/detect', methods=['GET', 'POST'])
def detect(): #この機能は、「性別の検出」ボタンがクリックされるとすぐにトリガーされます
padding = 20
warning = ""
gender = age = ""
global imagename, image_path
if request.method == 'POST':
small_frame, frame = resize_image(image_path)
frameFace, bboxes = getFaceBox(faceNet, small_frame) # ここで顔を検出
if not bboxes:
warning = "No Face Detected" # 顔はあるが検出されない場合は、「conf_threshold」パラメーターを変更できます。
else:
if len(bboxes) > 1:
warning = "More Than 1 Person Detected" # モデルはデフォルトですべての顔を検出します。 複数の場合、ここにエラーを表示します
else: # 画像ファイルで検出された顔が 1 つだけの場合
for bbox in bboxes:
# 性別検出に焦点を合わせるために、写真全体から顔の部分を切り取る
face = small_frame[max(0, bbox[1] - padding):min(bbox[3] + padding, frame.shape[0] - 1),
max(0, bbox[0] - padding):min(bbox[2] + padding, frame.shape[1] - 1)]
blob = cv2.dnn.blobFromImage(face, 1.0, (227, 227), MODEL_MEAN_VALUES, swapRB=False)
genderNet.setInput(blob) #性別を検出するために、dnn に顔写真 (スケーリングおよび正規化されたバージョン) が入力されます。
genderPreds = genderNet.forward() # ニューラル ネットワークの性別の重みが入力データに適用され、性別の予測が生成されます。
gender = genderList[genderPreds[0].argmax()] #argmax は、最高の性別確率値のインデックスを取得するために使用されます
#print(f"Gender : {gender}, conf = {genderPreds[0].max()}")
ageNet.setInput(blob) #年齢範囲を検出するために、dnn に顔写真 (スケーリングおよび正規化されたバージョン) が入力されます
agePreds = ageNet.forward() # ニューラルネットワークの年齢重みが入力データに適用され、年齢予測が生成されます
age = "Age: "+ ageList[agePreds[0].argmax()] #argmax は、最高年齢確率値のインデックスを取得するために使用されます
warning = u'\u2713' # これは✓文字です。 プロセスが正常に完了したことを示します。
#print(f"Age : {age}, conf = {agePreds[0].max()}")
return render_template('index.html', image_path=image_path, gender=gender, age=age, warning=warning, imagename=imagename)
if __name__ == '__main__':
app.run()
作ったもの
顔の写真から性別と年齢を予想できるアプリです。
また、今回はざっくりと手始めに作成してみましたが、もう一つ作り込んだアプリを制作予定です!