Go 언어로 여러 이미지를 세로로 연결

0. 소개



Go 언어를 사용하여 여러 장의 이미지 연결을 자동화하고 싶었기 때문에 Go 이미지 패키지를 사용하여 구현했습니다.

다른 Qiita 기사나 블로그 기사등도 찾았습니다만, 일본어로의 기사는 보이지 않았기 때문에, 간단한 내용입니다만 써 남기고 싶습니다.

1. 요구 사항


  • 입력 : 로컬에 저장된 두 개 이상의 이미지




  • 출력 : 입력 된 여러 이미지를 세로로 연결된 단일 이미지



  • 2. 알고리즘 요약


  • 다중 이미지로드
  • 출력 이미지의 가로 및 세로 폭 계산
  • 가로 폭: 입력 이미지의 가로 폭 최대값
  • 세로 폭: 입력 이미지의 세로 폭 합계

  • 위의 가로 및 세로 폭을 기반으로 빈 출력 이미지 생성
  • 이미지를 하나씩 출력 이미지에 씁니다

  • 3. 구현



    여러 이미지 로드



    로드는 image package의 image.Decode()image.DecodeConfig() 를 이용해 실시합니다.
    // path: input image file path
    func getImage(path string) (image.Image, error) {
        file, err := os.Open(path)
        if err != nil {
            return nil, err
        }
        defer file.Close()
    
        img, _, err := image.Decode(file)
        if err != nil {
            return nil, err
        }
    
        return img, nil
    }
    
    func getImageConfig(path string) (image.Config, error) {
        file, err := os.Open(path)
        if err != nil {
            return image.Config{}, err
        }
        defer file.Close()
    
        config, _, err := image.DecodeConfig(file)
        if err != nil {
            return image.Config{}, err
        }
    
        return config, nil
    }
    

    이번에는 image.Config 중 Width 및 Height를 반복적으로 사용하기 위해 myImage struct를 정의했습니다.
    type myImage struct {
        img    image.Image
        width  int
        height int
    }
    
    func getMyImage(path string) (myImage, error) {
        img, err := getImage(path)
        if err != nil {
            return myImage{}, err
        }
    
        config, err := getImageConfig(path)
        if err != nil {
            return myImage{}, err
        }
    
        return myImage{img, config.Width, config.Height}, nil
    }
    

    실은 여기에 하나 빠져 포인트가 있고, os.Open() 한 file 에 대해 아래와 같이 image.Decode() 와 image.DecodeConfig() 하면 unknown format 와 에러가 됩니다.
    file, _ := os.Open(path)
    img, _, _ := image.Decode(file)
    config, _, _ := image.DecodeConfig(file) // image: unknown format
    

    참고: htp // 곧 03. 하테나 bぉg. 코m/엔트리/2016/06/23/105152

    이번에 취급하는 화상은 고도 10장 정도이므로 2번 file 을 Open 하는 택을 취했습니다만, 요건에 따라서는 넥이 될지도 모릅니다.

    출력 화상의 가로폭·세로폭의 산출


  • 가로 폭
  • func getMaxWidth(imgs []myImage) int {
        var res int
        for _, img := range imgs {
            if res < img.width {
                res = img.width
            }
        }
    
        return res
    }
    
  • 세로 폭
  • func getSumHeight(imgs []myImage) int {
        var res int
        for _, img := range imgs {
            res += img.height
        }
    
        return res
    }
    

    위의 가로 및 세로 너비를 기반으로 빈 출력 이미지 생성


    image.NewRGBA를 사용하여 빈 출력 이미지를 생성합니다.
    // imgs: []myImage
    outImgWidth := getMaxWidth(imgs)
    outImgHeight := getSumHeight(imgs)
    
    outImg := image.NewRGBA(image.Rect(0, 0, outImgWidth, outImgHeight))
    

    이미지를 한 장씩 출력 이미지에 씁니다.


    image/draw package draw.Draw()를 사용하여 이전 출력 이미지에 읽은 이미지를 씁니다.
    pos := 0
    for _, img := range imgs {
        rect := image.Rect(0, pos, img.width, pos+img.height)
        draw.Draw(outImg, rect, img.img, image.Point{0, 0}, draw.Over)
        pos += img.height
    }
    

    각 설명
  • pos : 출력 이미지에서 이전에 쓴 이미지의 하단 가장자리의 y 좌표를 기록하고 다음 쓰기의 시작 y 좌표로 사용합니다
  • rect : draw.Draw에 전달하는 인수로 출력 이미지의 어느 범위에 쓸지를 지정합니다
  • image.Point{0, 0}: 입력 이미지의 어느 좌표로부터 기입할지를 draw.Draw 에 전합니다. ex) "이미지의 하반부 만 쓰고 싶다"면 image.Point{0, img.height/2}



  • 이상의 연결된 이미지를 생성하는 구현을 정리하면 다음과 같이 됩니다.
    func createConcatImage(imgs []myImage) *image.RGBA {
        outImgWidth := getMaxWidth(imgs)
        outImgHeight := getSumHeight(imgs)
    
        outImg := image.NewRGBA(image.Rect(0, 0, outImgWidth, outImgHeight))
    
        pos := 0
        for _, img := range imgs {
            rect := image.Rect(0, pos, img.width, pos+img.height)
            draw.Draw(outImg, rect, img.img, image.Point{0, 0}, draw.Over)
            pos += img.height
        }
    
        return outImg
    }
    

    4. 결론



    이상이 복수장 화상을 연결하는 실장이 됩니다.
    요구 사항의 사정상 Github 등에서 공유하기 힘들었기 때문에, 이미지 연결의 구현 부분을 Go Playground 로 공유합니다. (그러나 이미지를 로드해야 하므로 그대로 실행할 수 없습니다.)

    낭문 · 장문에 사귀어 주셔서 감사합니다.
    조금이라도 도움이되면 다행입니다.

    좋은 웹페이지 즐겨찾기