見出し画像

年齢・性別判定アプリ

自己紹介

はじめまして!
私はアプリ開発の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()

作ったもの

顔の写真から性別と年齢を予想できるアプリです。

また、今回はざっくりと手始めに作成してみましたが、もう一つ作り込んだアプリを制作予定です!

いいなと思ったら応援しよう!