본문 바로가기
DB 공부하기

Mini Project_딥러닝 활용 닮은 연예인 찾기 Django페이지 구현

by 보랏 2023. 5. 22.

안녕하세요. 보랏입니다. 

 

최근 1주일동안 mini_project를 진행하여 블로그 글이 없었는데, 

이제 막 지난주 금요일에 마무리되어 진행하였던 프로젝트 내용을 공유드리려고 합니다.

 

저희 팀에서 진행했던 프로젝트는 딥러닝(VGG16모델)을 활용하여 Django페이지를 

구현하는 것이었습니다.

 

저의 역할은 VGG16모델링을 담당하였고, Django페이지 부분 수정을 진행하였습니다.

 

그럼 바로 진행했던 프로젝트 정보에 대해서 전달드리도록 하겠습니다.

 


VGG16모델링

 

 

1. 프로젝트 개요 

  • 전이학습 활용 연예인 얼굴 분류 모델(.h5)생성
  • Django로 이미지 업로드 시 닮은 연예인을 분류하는 홈페이지 생성

 

2. 전체 구조 

face
 ┣ mysite
 ┃ ┣ config
 ┃ ┃ ┣ __pycache__
 ┃ ┃ ┃ ┣ __init__.cpython-310.pyc
 ┃ ┃ ┃ ┣ __init__.cpython-39.pyc
 ┃ ┃ ┃ ┣ settings.cpython-310.pyc
 ┃ ┃ ┃ ┣ settings.cpython-39.pyc
 ┃ ┃ ┃ ┣ urls.cpython-310.pyc
 ┃ ┃ ┃ ┣ urls.cpython-39.pyc
 ┃ ┃ ┃ ┣ wsgi.cpython-310.pyc
 ┃ ┃ ┃ ┗ wsgi.cpython-39.pyc
 ┃ ┃ ┣ __init__.py
 ┃ ┃ ┣ asgi.py
 ┃ ┃ ┣ settings.py
 ┃ ┃ ┣ urls.py
 ┃ ┃ ┗ wsgi.py
 ┃ ┣ star
 ┃ ┃ ┣ __pycache__
 ┃ ┃ ┃ ┣ __init__.cpython-310.pyc
 ┃ ┃ ┃ ┣ __init__.cpython-39.pyc
 ┃ ┃ ┃ ┣ admin.cpython-310.pyc
 ┃ ┃ ┃ ┣ admin.cpython-39.pyc
 ┃ ┃ ┃ ┣ apps.cpython-310.pyc
 ┃ ┃ ┃ ┣ apps.cpython-39.pyc
 ┃ ┃ ┃ ┣ forms.cpython-310.pyc
 ┃ ┃ ┃ ┣ models.cpython-310.pyc
 ┃ ┃ ┃ ┣ models.cpython-39.pyc
 ┃ ┃ ┃ ┣ urls.cpython-310.pyc
 ┃ ┃ ┃ ┣ urls.cpython-39.pyc
 ┃ ┃ ┃ ┣ views.cpython-310.pyc
 ┃ ┃ ┃ ┗ views.cpython-39.pyc
 ┃ ┃ ┣ migrations
 ┃ ┃ ┃ ┣ __pycache__
 ┃ ┃ ┃ ┃ ┣ __init__.cpython-310.pyc
 ┃ ┃ ┃ ┃ ┗ __init__.cpython-39.pyc
 ┃ ┃ ┃ ┗ __init__.py
 ┃ ┃ ┣ .DS_Store
 ┃ ┃ ┣ __init__.py
 ┃ ┃ ┣ admin.py
 ┃ ┃ ┣ apps.py
 ┃ ┃ ┣ forms.py
 ┃ ┃ ┣ models.py
 ┃ ┃ ┣ team3_new.h5
 ┃ ┃ ┣ tests.py
 ┃ ┃ ┣ urls.py
 ┃ ┃ ┗ views.py
 ┃ ┣ static
 ┃ ┃ ┣ assets
 ┃ ┃ ┃ ┣ img
 ┃ ┃ ┃ ┃ ┣ .DS_Store
 ┃ ┃ ┃ ┃ ┣ bg-callout.jpg
 ┃ ┃ ┃ ┃ ┣ bg-masthead.jpg
 ┃ ┃ ┃ ┃ ┣ portfolio-1.jpg
 ┃ ┃ ┃ ┃ ┣ portfolio-2.jpg
 ┃ ┃ ┃ ┃ ┣ portfolio-3.jpg
 ┃ ┃ ┃ ┃ ┗ portfolio-4.jpg
 ┃ ┃ ┃ ┣ .DS_Store
 ┃ ┃ ┃ ┗ favicon.ico
 ┃ ┃ ┣ css
 ┃ ┃ ┃ ┣ style.css
 ┃ ┃ ┃ ┗ styles.css
 ┃ ┃ ┣ js
 ┃ ┃ ┃ ┗ scripts.js
 ┃ ┃ ┗ .DS_Store
 ┃ ┣ templates
 ┃ ┃ ┣ index.html
 ┃ ┃ ┗ result.html
 ┃ ┣ .DS_Store
 ┃ ┣ db.sqlite3
 ┃ ┣ manage.py
 ┃ ┗ requirements.txt
 ┗ .DS_Store

 

 

2. 모델 생성(VGG16)

# 라이브러리 임포트
import tensorflow as tf
from tensorflow.keras.preprocessing import image
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.image import pad_to_bounding_box
from tensorflow.image import central_crop
from tensorflow.image import resize


#이미지 불러오기
bgd = image.load_img('./train/train_1/4.jpg')
bgd_vector = np.asarray(image.img_to_array(bgd))
bgd_vector = bgd_vector/255
 
#이미지 형태 확인 
bgd_vector.shape
 
#이미지 확인 
plt.imshow(bgd_vector)
plt.show()
  • 이미지 파일을 numpy배열로 변환하여 np.asarray()함수를 사용하여 배열로 변환
  • bad_vector 배열의 모든 원소를 255로 나누어서 0~1범위로 정규화

 

from tensorflow.keras.applications.vgg16 import VGG16
 
#weight, include_top 파라미터 설정 
model = VGG16(weights='imagenet', include_top=True)
model.summary()
  • TensorFlow의 keras라이브러리에서 vgg16모델을 훈련
  • weight : 사전에 훈련된 가중치를 지정하고 'imagenet'을 지정하면 imageNet 데이터셋으로 사전 훈련된 가중치를 사용
  • include_top : 모델의 최상위에 있는 완전 연결 레이어를 포함여부 결정(True = 포함)

 

3. 이미지 전처리 및 정규화

 

from tensorflow.keras.applications.vgg16 import preprocess_input
from tensorflow.keras.preprocessing import image
 
import matplotlib.pyplot as plt
from PIL import Image
from tensorflow.keras.applications.imagenet_utils import decode_predictions
from tensorflow.keras.applications.imagenet_utils import preprocess_input
import numpy as np
#from google.colab import drive
#drive.mount('/content/gdrive')
 
 
img = Image.open('./train/train_1/4.jpg')
img.size
plt.imshow(np.asarray(img))
  • import preprocess_input : vgg16모델에 입력하기 전에 이미리를 전처리
  • import image : 이미지를 전처리하기 위한 'image'모듈 호출
  • decode_predictions : vgg16모델의 출력을 해석하여 가장 가능성 있는 클래스 레이브을 가져오기

 

#VGG16이 입력받는 이미지크기 확인
model.layers[0].input_shape
 
#이미지 리사이즈
target_size = 224
img = img.resize((target_size, target_size)) # resize from 280x280 to 224x224
plt.imshow(np.asarray(img))
 
img.size #변경된 크기 확인
#numpy array로 변경
np_img = image.img_to_array(img)
np_img.shape  #(224, 224, 3) 
 
#4차원으로 변경 
img_batch = np.expand_dims(np_img, axis=0)
img_batch.shape
--> (1, 224, 224, 3)
  • img이미지를 img_to_array()함수를 이용하여 numpy배열로 변환하여 np_img변수에 할당
  • np.expand_dims(np_img, axis = 0)를 통해 첫 번째 축을 추가하여 np_img배열의 차원을 4차원으로 생성
  • (1,224,224,3) : 이미지 배치의 개수, 이미지 높이, 이미지 너비, 채널 수
#feature normalization
pre_processed = preprocess_input(img_batch)
pre_processed

 

y_preds = model.predict(pre_processed)
 
y_preds.shape  # 종속변수가 취할 수 있는 값의 수 = 1000
 
np.set_printoptions(suppress=True, precision=10)
y_preds
 
#가장 확률이 높은 값
np.max(y_preds)
  • model에 입력 이미지 pre_processed를 전달하여 예측 수행하고, model.predict()함수는 입력 이미지에 대한 예측 결과를 반환
  • y_preds.shape에서 배열의 크기를 확인하면 (1,1000)으로 표시되는데 여기서 1은 입력 이미지의 배치 크기, 1000은 vgg16모델이 imagenet데이터셋에 대해 분류할 수 있는 클래스 개수 표시
  • np.set_printoptions(suppress=True, precision=10) : numpy 배열 출력 옵션 설정 (suppress = True - 지수 표기법 사용하지 않음, precision = 10 - 소수점 아래 10자리까지 출력)
  • np.max(y_preds) : y_preds배열에서 가장 큰 값, 가장 높은 확률을 출력

 

4. 모델 생성

TRAIN_DATA_DIR = './train'
VALIDATION_DATA_DIR = './validation'
TEST_DATA_DIR = './test'
 
TRAIN_SAMPLES = 800*2
VALIDATION_SAMPLES = 400*2 
NUM_CLASSES = 2
IMG_WIDTH, IMG_HEIGHT = 224, 224
BATCH_SIZE = 64

train_datagen = ImageDataGenerator(preprocessing_function=preprocess_input,
                                   rotation_range=20,
                                   width_shift_range=0.2,
                                   height_shift_range=0.2,
                                   zoom_range=0.2)
 
val_datagen = ImageDataGenerator(preprocessing_function=preprocess_input)
  • preprocessing_function 매개변수를 통해 이미지 전처리 함수를 사용하여 preprocess_input함수로 vgg16모델에 적합한 형태로 전처리
  • 이미지 회전, 가로/세로 이동, 확대/축소 등의 데이터 증강 수행
train_generator = train_datagen.flow_from_directory(TRAIN_DATA_DIR,
                                                    target_size=(IMG_WIDTH,
                                                                 IMG_HEIGHT),
                                                    batch_size=BATCH_SIZE,
                                                    shuffle=True,
                                                    seed=12345,
                                                    class_mode='categorical')
 
validation_generator = val_datagen.flow_from_directory(
    VALIDATION_DATA_DIR,
    target_size=(IMG_WIDTH, IMG_HEIGHT),
    batch_size=BATCH_SIZE,
    shuffle=False,
    class_mode='categorical')

train_generator(훈련 데이터용 모델 생성)

  • TRAIN_DATA_DIR경로에 있는 데이터 로드하여 입력 이미지 크기(target_size), 배치에 포함할 이미지 개수(batch_size) 설정
  • 다중분류를 위해 categorical로 설정

validation_generator(검증 데이터용 모델 생성)

  • 데이터 경로를 VALIDATION_DATA_DIR로 설정하고 훈련 데이터용 모델과 동일하게 매개변수 설정

 

5. 최종 모델 생성

def model_maker():
    base_model = VGG16(include_top=False, input_shape=(IMG_WIDTH, IMG_HEIGHT, 3))
    print(len(base_model.layers))
 
    for layer in base_model.layers[:]:
        layer.trainable = False
 
    input = Input(shape=(IMG_WIDTH, IMG_HEIGHT, 3))
    custom_model = base_model(input)
    custom_model = GlobalAveragePooling2D()(custom_model)
    custom_model = Dense(32, activation='relu')(custom_model)
    predictions = Dense(NUM_CLASSES, activation='softmax')(custom_model)
    
    return Model(inputs=input, outputs=predictions)
model_final = model_maker()
model_final.summary()
  • 최상위 레이어(nclude_top=False)를 포함하지 않도록 설정
  • for layer in base_model.layers[:]: layer.trainable = False : 기본 모든 레이어를 동결하여 레이어의 가중치가 업데이드 되지 않도록 설정(기본 모델의 훈련을 방지)
  • custom_model에 입력 레이어를 기반으로 기본 모델 적용하고, GlobalAveragePooling2D()(custom_model)를 통해 차원축소 진행
  • Dense(32, activation='relu')(custom_model) : 전역 평균 풀링 이후 완전 연결 레이어 추가(뉴런 32개, ReLu활성화 함수 사용)
  • Dense(NUM_CLASSES, activation='softmax')(custom_model) : 클래스 개수 표시 후 'softmax'활성화 함수를 사용하여 클래스별 확률 계산

 

6. 최종 모델 컴파일

model_final.compile(loss='categorical_crossentropy',
              optimizer=tf.keras.optimizers.Adam(0.001),
              metrics=['acc'])
 
history = model_final.fit(
    train_generator,
    steps_per_epoch=TRAIN_SAMPLES // BATCH_SIZE, # number of updates
    epochs=10,
    validation_data=validation_generator,
    validation_steps=VALIDATION_SAMPLES // BATCH_SIZE)
  • 손실함수(loss)는 categorical_crossentropy를 사용하고 학습률은 0.001로 설정
  • 훈련 데이터와 검증 데이터를 사용하여 모델을 학습하고, 10 에포크로 fit함수를 통해 훈련하고 history에 학습결과 저장

테스트 데이터 모델 생성

test_datagen = ImageDataGenerator(preprocessing_function=preprocess_input)
 
test_generator = val_datagen.flow_from_directory(
    TEST_DATA_DIR,
    target_size=(IMG_WIDTH, IMG_HEIGHT),
    batch_size=BATCH_SIZE,
    shuffle=False,
    class_mode='categorical')
model_final.evaluate(test_generator, steps=800 // BATCH_SIZE)

 

prediction = model_final.predict(preprocessed_img)
print(np.array(prediction[0]))
  • 전처리된 사진과 테스트 데이터 모델과 비교 시 prediction배열의 첫 번째 원소(확률 값) 출력

모델 생성 시 진행 후 미흡한 점

  • 사진 데이터 수집 시 크롤링 함수를 구현하여 진행하였는데, 연예인 사진이 얼굴이 정확한 사진(데이터셋) 부족으로 모델의 학습이 부족함
  • 얼굴 사진 라이브러리 확인 및 함수 구현 방법으로 대체 필요
  • cpu로 처리하여 처리 속도 부족으로 aws의 머신러닝 처리기 등 GPU가 지원되는 방법으로 진행 필요

 

 


Django 홈페이지 구현

 

1. Django 프로젝트 시작

django-admin startproject config .
django-admin startapp star

 

2. config - settings.py 설정

# settings.py
ALLOWED_HOSTS = ['?.??.???.???']


INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'bootstrap4',
    'star',
    
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'team3',
        'USER': '????',
        'PASSWORD': '?????',
        'HOST': '?.??.???.???',
        'PORT': '3306'
    }
}
]

STATIC_URL = 'static/'
STATICFILES_DIRS = [
    BASE_DIR / 'static',
    os.path.join(BASE_DIR, 'static'),
    os.path.join(BASE_DIR, 'static', 'assets'),
    os.path.join(BASE_DIR, 'static', 'assets', 'img'),
    ]
  • DATABASES는 서버에서 실행하는 mysql로 연결
  • ALLOWED_HOSTS는 강사님이 제공해준 EC2서버 주소로 설정
  • STATIC폴더 생성 및 url 설정(기본 static/)
    - static : 이미지 분류 결과 연예인 사진 저장
    - assets-img : html 스타일 jpg 저장
    -css : 기본 style.css 파일 저장

 

3. config - urls.py

from django.contrib import admin
from django.urls import path, include


urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('star.urls')), 
]
  • ''로 시작하는 모든 url은 star폴더 안의 urls.py파일 안에서 관리하도록 설정

 

4. views.py

import os
from PIL import Image
from .models import Post
from django.shortcuts import render
from django.http import HttpResponse
from .forms import ImageUploadForm
from .models import ImageClassifier,Post
import numpy as np
import glob
# Create your views here.

def classify_image(request):
    if request.method == 'POST':
        form = ImageUploadForm(request.POST, request.FILES)
        if form.is_valid():
            image = form.cleaned_data['image']
            # 모델 경로 설정
            model_path = 'star/team3_new.h5'  # 실제 모델 파일의 경로로 수정
            # 이미지 분류기 초기화
            classifier = ImageClassifier(model_path)
            # 이미지 분류
            result = classifier.classify_image(image)
            Post.objects.create(confidence=result['confidence'], result=result['class_label'])
            return render(request, 'result.html', {'result': result})
    else:
        form = ImageUploadForm()
    return render(request, 'index.html', {'form': form})

def rank(request):
    results = Post.objects.all()
    return render(request, 'ranking.html', {'results': results})

def sub_menu_1(request):
    return render(request, 'sub_menu_1.html')
  • PTL.Image : 이미지 처리 모듈
  • .models.py에서 ImageClassifier, Post 모델 호출
  • .forms.py에서 ImageUploadForm 폼 호출

classify_image 함수 생성

  • 이미지 분류 과정을 처리하기 위한 함수
  • 이미지 파일을 POST 방식으로 받았을 때 ImageUploadForm 폼이 유효할 때 폼의 클린 데이터에서 이미지를 가져옴
  • model_path 변수의 이미지 분류기 모델 파일 경로로 설정
  • ImageClassifier(model_path)를 classifier객체로 저장하고 classify_image메서드를 호출하여 이미지를 매개변수로 전달
  • 분류 결과는 result에 저장되고 결과로 도출된 확률과 연예인 이름은 db에 저장 후 result.html템플릿에 렌더링되서 분류 결과 표시
  • sub_menu_1.html 템플릿을 렌더링

 

5. models.py

import tensorflow as tf
from django.db import models
from PIL import Image
import numpy as np
import os

class ImageClassifier:
    def __init__(self, model_path):
        self.model = tf.keras.models.load_model(model_path)
    def classify_image(self, image):
        class_label=['강해린',
'김채원',
'뉴진스 하니',
'안유진',
'아린',
'장원영',
'카즈하',
'차은우',
'방탄소년단 뷔',
'정준하',
 '정형돈',
 '서강준',
 '카리나',
 '마동석',
 '박보검',
 '손석구',
 '아이브 가을',
 '이광수',
 '침착맨',
 '르세라핌 사쿠라',]
        img = Image.open(image)
        img = img.resize((224, 224))  # 모델에 맞는 이미지 크기로 조정
        img = np.array(img) / 255.0  # 이미지 정규화
        img = np.expand_dims(img, axis=0)  # 배치 차원 추가
        predictions = self.model.predict(img)
        pred_class = np.argmax(predictions, axis=1)
        class_index = np.argmax(predictions[0])  # 가장 높은 확률을 가진 클래스 인덱스
        confidence = predictions[0][class_index]  # 분류 확률
        result = class_label[pred_class[0]]
        result = {
            'class_label': result,
            'confidence': confidence,
        }
        return result

class Post(models.Model):
    confidence = models.FloatField()
    result = models.CharField(max_length=100)

    def __str__(self):
        return self.result
  • tensorflow(딥러닝 프레임워크), PIL.Image(이미지 처리),numpy 등 모듈 호출

ImageClassifer 클래스 생성

  • model_path(star/team3_new.h5)를 받아서 해당 경로의 모델을 로드 후 self.model에 저장
  • classify_image 메서드는 이미지를 분류하는 역할로 이미지를 받아, 사전에 정의된 클래스 레이블을 사용하여 이미지를 분류
  • img로 image를 받아 VGG에 적합한 244x244로 크기를 재조정하고 0~1사이로 정규화
  • 4차원 배열로 만들기 위해 배치차원을 추가하고, 예측값을 얻어 가장 높은 확률을 가진 클래스 인덱스를 찾고 해당 클래스의 레이블과 분류 확률을 반환

Post 클래스 생성

  • 데이터베이스의 Post 모델을 생성하여 확률(confidence)은 실수, 이름은(result) 문자열로 저장하고, str메서드로 result필드값을 반환

 

6. forms.py

from django import forms
class ImageUploadForm(forms.Form):
    image = forms.ImageField()
  • 이미지를 입력받는(ImageField) form 생성

 

7. star - urls.py

from django.urls import path, include
from . import views
from star.views import classify_image

app_name = 'star'

urlpatterns = [
    path('', classify_image, name='classify_image'),
    path('sub_menu_1/', views.sub_menu_1, name='sub_menu_1'), 
]
  • app_name을 star로 설정하고 views.py에서 classify_image 함수 호출
  • 'http://도메인/' url에 접속하면 'classify_image' 함수가 실행되고 패턴이름은 함수 이름과 동일하게 설정
  • 'http://도메인/sub_menu_1' url에 접속 시, sub_menu_1 함수 실행

 

8. templates 설정

index.html

        <nav id="sidebar-wrapper">
            <ul class="sidebar-nav">
                <style>
                    .sub-menu {
                        display: none;
                    }
                    .sidebar-nav-item:hover .sub-menu {
                        display: block;
                    }
                </style>                
                <li class="sidebar-nav-item"><a href="{% url 'star:sub_menu_1' %}">About</a>
                </li>
                <li class="sidebar-nav-item"><a href="{% url 'star:classify_image' %}">닮은꼴 찾기</a></li>
            </ul>
        </nav>
  • sub_menu와 sidebar쪽에 스타일을 설정하고,sidebar_nav_item 쪽에 sub_menu1와 classify_image함수로 연결되는 url 지정
<body>
    <h5 style="text-align: center;"> ※ '.png' 파일은 안됩니다! ※</h5>
    <form method="POST" enctype="multipart/form-data" style="text-align: center;">
        {% csrf_token %}
        {{ form }}
        <div class="button-container">
            <button type="submit" class="btn btn-primary btn-xl">이미지 분류하기</button>
        </div>
    </form>
</body>
  • form태그는 이미지를 업로드하여 분류하기 위한 폼을 표시하고, method 속성은 POST로 설정되어 잇어 폼 데이터는 POST요청으로 서버에 전송, enctype속성은 multipart/form-data로 설정되어 있어 파일 업로드를 지원

 

result.html

<!-- result.html -->
{%load static%}
<!DOCTYPE html>
<html>
<header>
	<meta charset="UTF-8">
	<title>분류 결과</title>
	<style>
		body {
			display: flex;
			flex-direction: column;
			justify-content: top center;
			align-items: center;
			height: 100vh;
			text-align: center;
		}
		.return-button {
			display: inline-block;
			padding: 10px 20px;
			background-color: #006242;
			color: white;
			text-decoration: none;
			border-radius: 5px;
			font-size: 16px;
			margin-top: 20px;
		}

		.return-button:hover {
			background-color: #4CAF50;
		}

		.profile-image {
            width: 400px; /* 원하는 너비로 조정 */
            height: auto; /* 너비에 맞춰 자동으로 높이 조정 */
        }
		</style>
	</style>
	
</header>

<body>
   

    <h1>분류 결과</h1>
    <p>예측 결과: 대박 미친 {{ result.class_label }} 입니다...</p>
    <img class="profile-image" src="{% static result.class_label|add:'.jpg' %}">

    <p><a href="{% url 'star:classify_image' %}" class="return-button">돌아가기</a></p>
</body>
{% comment %} 
<body>
	<h1>분류 결과</h1>
	<p>예측 결과: {{ result }}</p>
	<img class="profile-image" src="{% static result.class_label|add:'.jpg' %}">

	<p><a href="http://127.0.0.1:8000/star/classify/">돌아가기</a></p>
</body>
</html> {% endcomment %}
  • {{result.class_label}}에서 Django뷰에서 전달된 result의 class_label 속성을 출력
  • src 속성에 result.class_label 변수에 .jpg확장자를 추가한 경로가 들어감

 

VGG16모델에서 .png파일을 인식하지 못하는 이유

  • VGG16모델은 RGB이미지를 입력으로 사용하고, png파일은 RGBA형식으로 저장(=이미지에 투명도 채널을 추가하는 것)
  • VGG16모델은 투명도 채널을 처리할 수 없어 .png파일을 인식하지 못함

 

<!-- sub_menu_1.html -->
<!DOCTYPE html>
<html>
<head>
    <title>아이템 선정</title>
    <!-- 필요한 스타일시트 및 스크립트 태그를 추가하세요 -->
    <style>
         body {
            background-image: url('/static/assets/img/bg-masthead.jpg');
            /* background-repeat: no-repeat; */
            background-size: cover;
        }
        .center-text {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            text-align: center;
            height: 100vh;
        }
        .container h1 {
            font-size: 48px;
            /* 화면 전체 높이에 맞게 조절 */
        }
        .header-container {
            text-align: center;
            margin-top: 50px;
        }
        .split-container {
            display: flex;
            justify-content: center;
            border-top: 1px solid black;
            flex-wrap: wrap;
            position: relative;
        }
        .split-container::before {
            content: "";
            width: 1px;
            background-color: black; /* 세로줄 색상 */
            position: absolute; /* 절대적 위치 설정 */
            top: 240px; /* 세로줄이 가로줄 위에 위치하도록 설정 */
            bottom: -400px; /* 세로줄이 가장 아래까지 표시되도록 설정 */
            left: 50%; /* 가운데로 위치하도록 설정 */
            transform: translateY(-50%); /* 세로줄을 정중앙에 위치시키도록 설정 */
            margin-top: 50px; /* 상단 여백 조정 */
        }
        .split-container .col {
            flex: 1;
            padding: 20px;
        }
        body, html {
            height: 100%;
            margin: 0;
            display: flex;
            flex-direction: column;
        }
        .content {
            flex: 1;
        }
    </style>
    
    
</head>

<body>
    <div class="header-container">
        <h1 >프로젝트 Face</h1>
        <h3 class="mb-5"><em>딥러닝을 이용하여 학습시킨 모델을 활용하여<br>닮은 유명인을 찾아주는 홈페이지 만들기</em></h3>
    </div>
    <div class="split-container">
        <div class="col" style="background-color: white;">
            <h2 style="text-align: center;">프로젝트명칭: Face</h2>
            <p>개발 인원: 6명</p>
            <p>개발 기간: 2023.05.12 - 2023.05.18</p>
            <p>간단한 소개: 딥러닝을 이용하여 학습시킨 모델을 홈페이지에 적용하여 사용자들이 사진을 올리면 닮은 연예인을<br>        보여주도록 함</p>
            <p>개발 언어: python</p>
            <p>형상 관리 틀: GitHub</p>
            <p>데이터베이스: SQLite</p>
            
        </div>
            <!-- 왼쪽 내용 -->
        <div class="col">
            <h2 style="text-align: center;">DB 설계</h2>
            <img src="/static/DB.jpg" alt="DB 설계 이미지" width="700">
        </div>
    </div>
</body>

</html>

 

 

현재 강사님께서 주신 서버가 내려가서 이미지가 부족하네요..
향후 추가하도록 하겠습니다.

 

그리고 이제 벨로그에도 해당 내용을 기재하였으니

코드를 보기 힘드시면 아래의 주소로 확인하시면 좋을 것 같습니다. 

https://velog.io/@nbac406/Project-%EB%8B%AE%EC%9D%80-%EC%97%B0%EC%98%88%EC%9D%B8-%EB%B6%84%EB%A5%98-Django-%EA%B5%AC%ED%98%84-1-%EB%AA%A8%EB%8D%B8-%EC%83%9D%EC%84%B1

 

https://velog.io/@nbac406/Project-%EB%8B%AE%EC%9D%80-%EC%97%B0%EC%98%88%EC%9D%B8-%EB%B6%84%EB%A5%98-Django-%EA%B5%AC%ED%98%84-2-Django-%EC%9B%B9-%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%A0%9C%EC%9E%91

 

이렇게 진행한 미니 프로젝트 소개를 마치겠습니다.

 

감사합니다.

'DB 공부하기' 카테고리의 다른 글

230523_DB복습  (0) 2023.05.23
230522_DB복습  (0) 2023.05.23
230503_DB복습  (0) 2023.05.03
230502_DB복습  (0) 2023.05.02
230427_DB복습  (0) 2023.04.27