본문 바로가기
과정/2차 AI프로젝트 서비스 개발

프로젝트 '재움' 개발과정(4)

by 줘요 2023. 10. 31.

안녕하세요!

오늘은 실시간으로 캡처된 이미지에 라벨을 달고 자세가 3번 연속 바뀌었을 때 데이터베이스에 수면이벤트가 저장되도록 해보겠습니다.

 

 

먼저 record.js에 함수를 추가해주겠습니다. 

 

이전에 작성하지 않았던 captureAndUploadFrame 함수입니다.

// 이미지 업로드 함수
function captureAndUploadFrame() {
  // HTML <canvas> 요소를 동적으로 생성합니다. 이 캔버스는 이미지를 그리기 위한 렌더링 대상이 됩니다.
  const canvas = document.createElement("canvas");
  // 캔버스 크기 설정
  canvas.width = videoElement.videoWidth;
  canvas.height = videoElement.videoHeight;

  canvas
    // 2D 그래픽 컨텍스트를 가져옵니다.
    .getContext("2d")
    // videoElement의 비디오 프레임을 캔버스에 그립니다.
    .drawImage(videoElement, 0, 0, canvas.width, canvas.height);

  // 캡처된 이미지를 Blob 형태로 변환
  canvas.toBlob(function (blob) {
    // Blob 데이터를 image(key)로 FormData에 추가
    const formData = new FormData();
    formData.append("image", blob, "captured_image.jpg");
    console.log("formData : ", formData);

    // FormData의 key 확인
    for (let key of formData.keys()) {
      console.log("key : ", key);
    }

    // FormData의 value 확인
    for (let value of formData.values()) {
      console.log("value : ", value);
    }

    // 서버로 이미지 데이터 전송 (fetch API 사용)
    sendImageToServer(formData);
  }, "image/jpg"); // jpg로 이미지 저장
}

 

바로 이어서 서버로 이미지 데이터를 전송해 주는 sendImageToServer 함수를 작성하겠습니다.

 

함수 안에 자세가 바뀌었을 때 조건문이 들어있습니다.

function sendImageToServer(formData) {
  fetch("/record/return_posture", {
    method: "POST",
    body: formData,
  })
    .then(function (response) {
      // 서버 응답이 JSON 형식일 경우
      return response.json();
    })
    .then(function (data) {
      console.log("서버에서 받은 데이터:", data);

      var currentPosture = data;

      console.log("현재 자세:", currentPosture);
      console.log("이전 자세:", latestPosture);
      // 이전 자세와 현재 자세를 비교하여 로직을 추가합니다.
      if (latestPosture == null) {
        // 최근 자세 정보 업데이트
        latestPosture = currentPosture;
      }
      if (latestPosture !== null) {
        if (latestPosture !== currentPosture) {
          // 자세가 바뀌었을 때 처리할 코드
          changedPostureCount++; // 자세 변경 횟수 증가
          console.log("다른 횟수:", changedPostureCount);

          if (changedPostureCount >= 3) {
            // 자세가 3번 이상 변경되었을 때 처리할 코드
            latestPosture = currentPosture;
            changedPostureCount = 0;
            callSavePostureApi(formData, data);
          }
        } else {
          // 자세가 같을 때
          changedPostureCount = 0; // 자세 변경 횟수 초기화
        }
      }
    })
    .catch(function (error) {
      console.error("이미지 업로드 중 오류 발생:", error);
    });
}

 

자세를 반환해 줄 함수를 api_record.py에 작성해 주겠습니다.

def process_image(image):
    # 이미지를 캡처하고 저장합니다.
    file_path = f"static/images/pose/{image.filename}"
    with open(file_path, "wb") as image_file:
        image_file.write(image.file.read())

    # 캡처한 이미지를 읽습니다.
    image = cv2.imread(file_path)

    # 랜드마크를 감지합니다.
    response = detect_pose(image, pose, display=False)

    if response is not None:
        landmarks_image, landmarks = response
        frame_height, frame_width, _ = landmarks_image.shape
        fps = 50

        # 랜드마크를 기반으로 레이블을 분류합니다.
        labeled_image, labels = classify_pose(landmarks, landmarks_image, fps, frame_height, frame_width, display=False)

        # 배열을 문자열로 바꾸기
        labels_str = ' '.join(labels)

        # 라벨링 되기 전 이미지는 삭제합니다.
        if os.path.exists(file_path):
            os.remove(file_path)

        return labels_str
@router.post("/record/return_posture")
async def upload_image(image: UploadFile):
    # 이미지 처리 함수 호출
    labels_str = process_image(image)  # 라벨 문자열 반환

    return labels_str

위 함수를 통해 자세 라벨을 서버로부터 넘겨받습니다.

 

자세가 3번 이상 변경되었을 때 불러올 callSavePostureApi 함수도 작성하겠습니다.

 

수면 이벤트를 저장하는 함수입니다.

// 이미지 저장 함수
function callSavePostureApi(imageData, data) {
  const sleepInfoId = document.getElementById("sleep_info_id").value;
  // FormData 객체를 생성합니다.
  const formData = new FormData();
  formData.append("sleep_info_id", sleepInfoId);
  formData.append("data", data);
  formData.append("image", imageData.get("image"), "captured_image.jpg");

  console.log("sleep_info_id:", sleepInfoId);
  console.log("data:", data);
  console.log("imageData:", imageData.get("image"));

  fetch("/record/event_save", {
    method: "POST",
    body: formData,
  })
    .then(function (response) {
      if (response.ok) {
        console.log("Image saved successfully.");
      } else {
        console.error("Error saving the image.");
      }
    })
    .catch(function (error) {
      console.error("Error saving the image:", error);
    });
}

 

이미지 저장 하기 전 개인정보를 위해 얼굴 모자이크 하는 함수를 작성해서 호출합니다.

import cv2
import mediapipe as mp

# 미디어파이프 초기화
mp_face_detection = mp.solutions.face_detection
mp_drawing = mp.solutions.drawing_utils
face_detection = mp_face_detection.FaceDetection(min_detection_confidence=0.30)

def detect_face(image, factor=15):
    # RGB 이미지로 변환
    frame_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    
    # 얼굴 감지 수행
    results = face_detection.process(frame_rgb)
    
    if results.detections:
        for detection in results.detections:
            # 감지된 얼굴의 바운딩 박스 정보 가져오기
            bboxC = detection.location_data.relative_bounding_box
            ih, iw, _ = image.shape
            x, y, w, h = int(bboxC.xmin * iw), int(bboxC.ymin * ih), int(bboxC.width * iw), int(bboxC.height * ih)

            # 얼굴 주위에 픽셀화 적용
            # 얼굴 영역 크기 조정
            face_roi = image[y:y+h, x:x+w]
            face_roi = cv2.resize(face_roi, (0, 0), fx=0.04, fy=0.04)  # 얼굴 크기 축소
            face_roi = cv2.resize(face_roi, (w, h), interpolation=cv2.INTER_NEAREST)  # 원래 크기로 확대
            image[y:y+h, x:x+w] = face_roi  # 원본 이미지에 픽셀화된 얼굴 영역 적용

    return image

 

수면이벤트를 저장할 때 형식과 사용할 쿼리도 작성해 줍니다.

 

shchemas.py

class SleepEventBase(BaseModel):
    sleep_info_id: int
    sleep_event: str
    event_time: str
    event_data_path: str

 

crud.py

def create_sleep_event(db: Session, sleep_event_data: schemas.SleepEventBase):
    db_sleep_event = model.SleepEvent(
        sleep_info_id=sleep_event_data.sleep_info_id,
        sleep_event=sleep_event_data.sleep_event,
        event_time=sleep_event_data.event_time,
        event_data_path=sleep_event_data.event_data_path,
    )
    db.add(db_sleep_event)
    db.commit()
    db.refresh(db_sleep_event)
    return db_sleep_event

 

이제 api_record.py에 /record/event_save를 post로 받는 함수를 작성하겠습니다.

@router.post("/record/event_save")
async def event_image_save(image: UploadFile, data: str = Form(...), sleep_info_id: int = Form(...), db: Session = Depends(get_db)):
    # 현재 날짜와 시간 생성
    current_date = datetime.now().strftime("%Y-%m-%d")
    current_time = datetime.now().strftime("%H:%M:%S")
    formatted_time = datetime.now().strftime("%H-%M-%S")

    # 파일 저장 이름 정의
    file_path = f"static/images/pose/{current_date}_{formatted_time}_{data}.jpg"
    
    with open(file_path, "wb") as image_file:
        image_file.write(image.file.read())

    # 이미지 로드
    image = cv2.imread(file_path)

    # 얼굴 감지 및 픽셀화 처리 함수 호출
    image = detect_face(image, factor=15)

    # 픽셀화 처리한 이미지 저장
    cv2.imwrite(file_path, image)

    sleep_event_data = SleepEventBase(
        sleep_info_id=sleep_info_id,
        sleep_event=data,
        event_time=current_time,
        event_data_path=file_path
    )

    db_sleep_event = create_sleep_event(db, sleep_event_data)

    return "Image saved successfully with pixelation"

 

이제 실제로 사용해 보면 자세가 3번 바뀌었을 때 모자이크된 이미지와 수면이벤트가 DB에 저장되는 것을 확인해 볼 수 있습니다.

 

 

댓글