-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #99 from Team-KeepGoing/feature/face
Feat :: 얼굴 인식 기능 개발
- Loading branch information
Showing
9 changed files
with
274 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
src/main/java/com/keepgoing/keepserver/domain/face/controller/FaceRecognitionController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package com.keepgoing.keepserver.domain.face.controller; | ||
|
||
import com.keepgoing.keepserver.domain.face.service.FaceRecognitionService; | ||
import com.keepgoing.keepserver.global.common.BaseResponse; | ||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RequestPart; | ||
import org.springframework.web.bind.annotation.RestController; | ||
import org.springframework.web.multipart.MultipartFile; | ||
|
||
@Tag(name = "얼굴인식", description = "얼굴인식 관련 api 입니다.") | ||
@RestController | ||
@RequestMapping("/face") | ||
@RequiredArgsConstructor | ||
public class FaceRecognitionController { | ||
|
||
private final FaceRecognitionService faceRecognitionService; | ||
|
||
@Operation(summary = "얼굴 인식", description = "얼굴을 인식합니다.") | ||
@PostMapping(value = "/compare", consumes = { MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.APPLICATION_JSON_VALUE }) | ||
public BaseResponse compareFaces(@RequestPart("image") MultipartFile image) { | ||
return faceRecognitionService.compareFaces(image); | ||
} | ||
|
||
@Operation(summary = "얼굴 등록", description = "얼굴을 등록합니다.") | ||
@PostMapping(value = "/register", consumes = { MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.APPLICATION_JSON_VALUE }) | ||
public BaseResponse registerFace(@RequestPart("email") String email, @RequestPart("image") MultipartFile image) { | ||
return faceRecognitionService.registerFace(email, image); | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
src/main/java/com/keepgoing/keepserver/domain/face/entity/Face.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.keepgoing.keepserver.domain.face.entity; | ||
|
||
import jakarta.persistence.*; | ||
import lombok.Getter; | ||
import lombok.Setter; | ||
|
||
@Getter | ||
@Setter | ||
@Entity | ||
@Table(name = "face") | ||
public class Face { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long id; | ||
|
||
@Column(nullable = false, unique = true) | ||
private String email; | ||
|
||
@Column(name = "s3_image_url") | ||
private String s3ImageUrl; | ||
} |
14 changes: 14 additions & 0 deletions
14
src/main/java/com/keepgoing/keepserver/domain/face/repository/FaceRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.keepgoing.keepserver.domain.face.repository; | ||
|
||
import com.keepgoing.keepserver.domain.face.entity.Face; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import org.springframework.stereotype.Repository; | ||
|
||
import java.util.Optional; | ||
|
||
@Repository | ||
public interface FaceRepository extends JpaRepository<Face, Long> { | ||
Optional<Face> findByEmail(String email); | ||
|
||
Optional<Face> findByS3ImageUrl(String s3ImageUrl); | ||
} |
10 changes: 10 additions & 0 deletions
10
src/main/java/com/keepgoing/keepserver/domain/face/service/FaceRecognitionService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.keepgoing.keepserver.domain.face.service; | ||
|
||
import com.keepgoing.keepserver.global.common.BaseResponse; | ||
import org.springframework.web.multipart.MultipartFile; | ||
|
||
public interface FaceRecognitionService { | ||
BaseResponse compareFaces(MultipartFile sourceImage); | ||
|
||
BaseResponse registerFace(String email, MultipartFile image); | ||
} |
84 changes: 84 additions & 0 deletions
84
src/main/java/com/keepgoing/keepserver/domain/face/service/FaceRecognitionServiceImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package com.keepgoing.keepserver.domain.face.service; | ||
|
||
import com.keepgoing.keepserver.domain.face.entity.Face; | ||
import com.keepgoing.keepserver.domain.face.repository.FaceRepository; | ||
import com.keepgoing.keepserver.domain.image.service.ImageService; | ||
import com.keepgoing.keepserver.global.common.BaseResponse; | ||
import com.keepgoing.keepserver.global.common.S3.S3Uploader; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.web.multipart.MultipartFile; | ||
import software.amazon.awssdk.services.rekognition.RekognitionClient; | ||
import software.amazon.awssdk.services.rekognition.model.CompareFacesRequest; | ||
import software.amazon.awssdk.services.rekognition.model.CompareFacesResponse; | ||
|
||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class FaceRecognitionServiceImpl implements FaceRecognitionService { | ||
|
||
private final ImageService imageService; | ||
private final RekognitionClient rekognitionClient; | ||
private final FaceRepository faceRepository; | ||
private final S3Uploader s3Uploader; | ||
|
||
@Override | ||
public BaseResponse compareFaces(MultipartFile sourceImage) { | ||
String tempImageUrl = null; | ||
|
||
try { | ||
tempImageUrl = s3Uploader.upload(sourceImage, "face-android"); | ||
|
||
List<String> studentImageUrls = imageService.getAllImageUrlsFromS3("upload"); | ||
|
||
for (String studentImageUrl : studentImageUrls) { | ||
CompareFacesRequest request = CompareFacesRequest.builder() | ||
.sourceImage(imageService.getS3Image(tempImageUrl)) // 임시 저장된 이미지 사용 | ||
.targetImage(imageService.getS3Image(studentImageUrl)) // 학생 등록 이미지 | ||
.similarityThreshold(80F) | ||
.build(); | ||
|
||
CompareFacesResponse response = rekognitionClient.compareFaces(request); | ||
|
||
if (!response.faceMatches().isEmpty()) { | ||
Optional<Face> matchedUser = faceRepository.findByS3ImageUrl(studentImageUrl); | ||
if (matchedUser.isPresent()) { | ||
return new BaseResponse(HttpStatus.OK, "얼굴 인식 성공", matchedUser.get().getEmail()); | ||
} | ||
} | ||
} | ||
|
||
return new BaseResponse(HttpStatus.OK, "얼굴 등록이 되지 않은 이용자입니다."); | ||
} catch (Exception e) { | ||
return new BaseResponse(HttpStatus.INTERNAL_SERVER_ERROR, "비교 중 오류가 발생했습니다.", e.getMessage()); | ||
} finally { | ||
if (tempImageUrl != null) { | ||
s3Uploader.removeFaceAndroidFile(tempImageUrl); | ||
} | ||
} | ||
} | ||
|
||
@Override | ||
public BaseResponse registerFace(String email, MultipartFile image) { | ||
try { | ||
Optional<Face> existingUser = faceRepository.findByEmail(email); | ||
if (existingUser.isPresent()) { | ||
return new BaseResponse(HttpStatus.CONFLICT, "이미 해당 이메일로 등록된 사용자가 있습니다."); | ||
} | ||
|
||
String s3ImageUrl = s3Uploader.upload(image,"upload"); | ||
|
||
Face newUser = new Face(); | ||
newUser.setEmail(email); | ||
newUser.setS3ImageUrl(s3ImageUrl); | ||
faceRepository.save(newUser); | ||
|
||
return new BaseResponse(HttpStatus.OK, "얼굴 등록 성공", email); | ||
} catch (Exception e) { | ||
return new BaseResponse(HttpStatus.INTERNAL_SERVER_ERROR, "얼굴 등록 실패", e.getMessage()); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
src/main/java/com/keepgoing/keepserver/global/util/AwsRekognitionClient.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package com.keepgoing.keepserver.global.util; | ||
|
||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; | ||
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; | ||
import software.amazon.awssdk.regions.Region; | ||
import software.amazon.awssdk.services.rekognition.RekognitionClient; | ||
import software.amazon.awssdk.services.s3.S3Client; | ||
|
||
@Configuration | ||
public class AwsRekognitionClient { | ||
|
||
@Value("${cloud.aws.credentials.accessKey}") | ||
private String accessKeyId; | ||
|
||
@Value("${cloud.aws.credentials.secretKey}") | ||
private String secretKey; | ||
|
||
@Value("${cloud.aws.region.static}") | ||
private String region; | ||
|
||
@Bean | ||
public RekognitionClient rekognitionClient() { | ||
AwsBasicCredentials awsCreds = AwsBasicCredentials.create(accessKeyId, secretKey); | ||
return RekognitionClient.builder() | ||
.credentialsProvider(StaticCredentialsProvider.create(awsCreds)) | ||
.region(Region.of(region)) | ||
.build(); | ||
} | ||
|
||
@Bean | ||
public S3Client s3Client() { | ||
AwsBasicCredentials awsCreds = AwsBasicCredentials.create(accessKeyId, secretKey); | ||
return S3Client.builder() | ||
.credentialsProvider(StaticCredentialsProvider.create(awsCreds)) | ||
.region(Region.of(region)) | ||
.build(); | ||
} | ||
} |