grpc로 이미지 주고 받기

박근수·2024년 4월 21일
0

ssafy_특화

목록 보기
2/2

Spring 서버와 Python 서버가 이미지 파일을 주고 받기로 했다.
서버 내부 통신이기 때문에 gRPC로 통신을 하기로 했다.

@Service
public class ImageService {
....

private final ManagedChannel channel
            = ManagedChannelBuilder.forTarget("localhost:9090").usePlaintext().build();

    private final CreateImageGrpc.CreateImageBlockingStub imageStub
            = CreateImageGrpc.newBlockingStub(channel);

    public ByteArrayResource sendImage(MultipartFile image, ImageOption imageOption) throws IOException {
        ByteString imageData = ByteString.copyFrom(image.getBytes());
        BufferedImage bufferedImage = null;
        Image.Options options = Image.Options.newBuilder()
                .setBackground(imageOption.getBackground())
                .setHair(imageOption.getHair())
                .setSuit(imageOption.getSuit()).build();

        Image.ProcessedImageInfo receiveData =
                this.imageStub.sendImage(Image.OriginalImageInfo.newBuilder()
                        .setOriginalImage(imageData)
                        .setOptions(options)
                        .build());

syntax = "proto3";

package com.ssafy.pjt.grpc;

service CreateImage {
  rpc sendImage (OriginalImageInfo) returns (ProcessedImageInfo);
}

message OriginalImageInfo {
  bytes originalImage = 1;
  Options options = 2;
}

message processedImage{
  string name = 1;
  bytes image = 2;
}


....

Controller가 Image를 보내주면 proto 포멧에 맞춰서 bytes로 변환해야한다. image 파일은 bytes로 날아오긴 했지만 타입이 MultipartFile이기 때문에 ByteString으로 변환해주고 Python 서버에 전송한다. Python 서버에서 이미지 처리가 끝나면 Spring서버에 bytes로 다시 보내준다.

(Client)MultipartFile ->(Spring) bytes ->(Python) bytes -> (Spring)

MutipartFile을 쉽게 byte[]로 변환하고 Python에서 이미지 처리를 했으니 Spring에서 byte[]파일을 받으면 쉽게 Image 파일로 변환할 수 있을 것 같았는데 그게 안됨

byte[]를 Image로 다시 복구시키는 작업이 필요했음.
byte를 width*height Image로 복구 시켜야하는데 파이썬으로 이미지를 처리하면 width, height 정보가 사라져서 Java가 눈치껏 복구할 수가 없고 그저 byte[]의 데이터로만 사용할 수가 있음. 그래서 width, height, 색타입을 주고 byte[]를 BufferedImage타입으로 만듬.
이후 픽셀에 맞는 색 주입.

gRPC로 받은 데이터를 복구하는 메소드 getBufferedImage 호출

 byte[] processedImageData = receiveData.getProcessedImage().toByteArray();
            ByteArrayResource byteArrayResource = getBufferedImage(processedImageData,768,1024);
            Image.ResponseUrl responseUrl = receiveData.getResponseUrl();

byte[] to ByteArrayResource로 복구

    private ByteArrayResource getBufferedImage(byte[] processedImageData,int width, int height) throws IOException {
        BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);

        // BufferedImage에 byte 배열 데이터 채우기
        int index = 0;
        for (int y = 0; y < bufferedImage.getHeight(); y++) {
            for (int x = 0; x < bufferedImage.getWidth(); x++) {
                int red = processedImageData[index++] & 0xFF;
                int green = processedImageData[index++] & 0xFF;
                int blue = processedImageData[index++] & 0xFF;

                // RGB 값으로 Pixel 생성 및 설정
                bufferedImage.setRGB(x, y, new Color(blue, green, red).getRGB());
            }
        }

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(bufferedImage, "jpg", baos);

        ByteArrayResource resource = new ByteArrayResource(baos.toByteArray());

        return resource;
    }

width와 height는 파이썬에서 만들기로한 이미지 사이즈를 미리 정해놓았음. static 변수로 설정해도 좋았을 것 같음.

profile
개성이 확실한편

0개의 댓글