๐ŸงฉImage Stitching ํ”„๋กœ์ ํŠธ

hyukimยท2020๋…„ 6์›” 21์ผ
4
post-thumbnail

๐Ÿ“–๊ฐœ์š”

์ด๋ฒˆ 4์›” ๋ง์— ์—๋ธŒ๋ฆฌํƒ€์ž„ ๊ฒŒ์‹œํŒ ๋ณด๋ฉด์„œ ์‹œ๊ฐ„ ์ข€ ๋ณด๋‚ด๊ณ  ์žˆ์—ˆ๋Š”๋ฐ ํŒŒ์ด์ฌ์œผ๋กœ ์ด๋ฏธ์ง€๋“ค์„ ๋ถ™์ด๊ณ  ์ด๋ฏธ์ง€๋กœ ๊ทธ๋ž˜ํ”„๋ฅผ ๋งŒ๋“œ๋Š” ์ž‘์—…์„ ํ•  ์‚ฌ๋žŒ์„ ์ฐพ๋Š”๋‹ค๋Š” ๊ธ€์„ ๋ดค๋‹ค. ๊ทธ๋ž˜์„œ ๊ทธ๋ƒฅ ์ธํ’‹์œผ๋กœ ๋“ค์–ด์˜ค๋Š” ์ด๋ฏธ์ง€ ํ•œ ๋‘, ์„ธ ๊ฐœ ์ •๋„ ์ž๋™์œผ๋กœ ๊ฐ€๋กœ๋กœ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฐ„๋‹จํ•œ ํ”„๋กœ๊ทธ๋žจ์ด๊ฒ ๊ฑฐ๋‹ˆ ํ•˜๊ณ  ์—ฐ๋ฝํ–ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์•Œ๊ณ  ๋ณด๋‹ˆ Open-CV๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์—ฌ๋Ÿฌ ์ด๋ฏธ์ง€์˜ ๊ณตํ†ต ๋ถ€๋ถ„์„ ์ฐพ์•„์„œ ์—ฐ๊ฒฐํ•˜์—ฌ ํฐ ์ตœ์ข… ์ด๋ฏธ์ง€๋ฅผ ๋งŒ๋“œ๋Š” ํ”„๋กœ์ ํŠธ์˜€๋‹ค.

์ซŒ ๊นŒ๋‹ค๋กœ์›Œ ๋ณด์—ฌ์„œ ํ•˜์ง€ ๋ง๊นŒ ํ–ˆ๋Š”๋ฐ, ์นœ๊ตฌ๊ฐ€ OpenCV์˜ ImageStitcher ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ผ๋Š”๊ฒŒ ์žˆ๋‹ค๊ณ  ํ•ด์„œ ์‚ฌ์šฉํ•ด๋ณด๋‹ˆ ์ฝ”๋“œ ๋ช‡ ์ค„์ด๋ฉด ๊ธˆ๋ฐฉ ์™„์„ฑ ๋๋‹ค. ๋ฏธ์„ธํ•œ ๊ฐ’๋“ค์€ ์ข€ ํ…Œ์ŠคํŠธํ•˜๋ฉด์„œ ๋งž์ถฐ๊ฐ€๋ฉด ๊ธˆ๋ฐฉ ํ•  ๊ฒƒ ๊ฐ™๋‹ค๊ณ  ์ƒ๊ฐํ•ด์„œ ์ˆ˜๋ฝํ–ˆ๋‹ค. (๊ทธ๋ ‡๊ฒŒ ๊ณ ๋‚œ์˜ ์‹œ์ž‘...)

์ด ํ”„๋กœ์ ํŠธ๋Š” ํ™˜๊ฒฝ๊ณตํ•™๊ณผ ๋Œ€ํ•™์›์—์„œ ์ง„ํ–‰ํ•˜๋Š” ์—ฐ๊ตฌ์ด๋‹ค. ์ด ์—ฐ๊ตฌ์˜ ๋ชฉํ‘œ๋Š” "๊ณต์ค‘์—์„œ ๋“œ๋ก ์œผ๋กœ ์ฐ์€ ํ˜ธ์ˆ˜ ์ด๋ฏธ์ง€๋ฅผ ์ธํ’‹์œผ๋กœ ์ƒ‰์ƒ ๊ฐ’์„ ํŒ๋ณ„ํ•˜์—ฌ ๋…น์กฐ์˜ ์œ„์น˜ ๋ฐ ์ •๋„๋ฅผ ํŒŒ์•…ํ•˜์—ฌ ๊ทธ๋ž˜ํ”„๋กœ ํ‘œํ˜„ํ•˜๋Š” ๊ฒƒ"์ด๋‹ค. ์ด ํ”„๋กœ์ ํŠธ์—์„œ ๋‚ด๊ฐ€ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ ์œผ๋กœ ๋งก์„ ๋ถ€๋ถ„์€ ํฌ๊ฒŒ Image Stitching๊ณผ Graph Making์˜ ๋‘ ํŒŒํŠธ๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ๊ฒ ๋‹ค.

๐ŸงฉImage Stitching

๐ŸŽฏ๋ชฉํ‘œ

  • ์—ฌ๋Ÿฌ ์ด๋ฏธ์ง€์˜ ๊ณตํ†ต ๋ถ€๋ถ„์„ ์ฐพ์•„์„œ ํ•˜๋‚˜์˜ ํฐ ์ด๋ฏธ์ง€๋กœ ํ•ฉ์นœ๋‹ค.

๐Ÿคทโ€โ™‚๏ธ๋ฌธ์ œ ํ•ด๊ฒฐ ์ˆœ์„œ

  • Image load
    • Image resize
  • Image stitching
    • ์ด๋ฏธ์ง€1๊ณผ ์ด๋ฏธ์ง€2์˜ ๊ฒน์น˜๋Š” ๋ถ€๋ถ„(matching point) ์ฐพ๊ธฐ
    • ์ด๋ฏธ์ง€1์„ ๊ธฐ์ค€์œผ๋กœ ์ด๋ฏธ์ง€2์˜ ๊ฒน์น˜๋Š” ๋ถ€๋ถ„์„ 2D ๋ณ€ํ˜•ํ•˜์—ฌ ๋ถ™์ด๊ธฐ
    • ๋ชจ๋“  ์ด๋ฏธ์ง€์— ๋Œ€ํ•ด์„œ ์œ„ ๋™์ž‘์„ ๋ฐ˜๋ณต

๐Ÿค”๋ฌธ์ œ ํ•ด๊ฒฐ

  • ์ด๋ฏธ์ง€ ๋กœ๋“œ

    • ๋กœ๋”ฉ : ์ž‘์—…๋Œ€์— ์ค€๋น„๋ฌผ์„ ์˜ฌ๋ฆฌ๋Š” ์ž‘์—…

    • ์ด๋ฏธ์ง€ ์šฉ๋Ÿ‰์ด ํฌ๋ฉด? ์ž‘์—…์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๊ฑฐ๋‚˜ ํ•œ ์ž‘์—…๋Œ€์—์„œ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌ ๋ชป ํ•  ์ˆ˜๋„ ์žˆ์Œ

    • ์ด๋ฏธ์ง€ ๋ฆฌ์‚ฌ์ด์ฆˆ : ๊ทธ๋Ÿด ๊ฒฝ์šฐ์— ์ด๋ฏธ์ง€ ํฌ๊ธฐ๋ฅผ ์ค„์—ฌ์„œ ๋น ๋ฅด๊ณ  ๋งŽ์€ ์–‘์„ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌ

  • Image Stitching

    • ์ด๋ฏธ์ง€ ์Šคํ‹ฐ์นญ : ์—ฌ๋Ÿฌ ์ด๋ฏธ์ง€์—์„œ ๊ณตํ†ต ๋ถ€๋ถ„์„ ์ฐพ์•„์„œ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒƒ

    • ๊ณตํ†ต ๋ถ€๋ถ„(match point)์ฐพ๊ธฐ : ๋‘ ์ด๋ฏธ์ง€์—์„œ ๊ณตํ†ต ๋ถ€๋ถ„์„ ์ฐพ๋Š”๋‹ค.

    • ๊ณตํ†ต ๋ถ€๋ถ„ ์—ฐ๊ฒฐ : ์—ฌ๋Ÿฌ ์ด๋ฏธ์ง€์—์„œ ๊ณตํ†ต ๋ถ€๋ถ„์„ ์ฐพ์•„์„œ 2D ๋ณ€ํ˜• ํ›„ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒƒ

    • 2D ๋ณ€ํ˜• : ํ‰๋ฉด์—์„œ ๋„ํ˜•์˜ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ณ€ํ˜•

    • 2D ๋ณ€ํ˜•์ด ํ•„์š”ํ•œ ์ด์œ  : ๊ฐ™์€ ์ด๋ฏธ์ง€๋ผ๋„ ์œ„์น˜๋‚˜ ๊ฐ๋„์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ๋ณด์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ๊ต์ •ํ•˜๊ธฐ ์œ„ํ•ด์„œ

    • ๋ชจ๋“  ์ด๋ฏธ์ง€์— ๋Œ€ํ•ด ๋ฐ˜๋ณต

โŒจ์ฝ”๋“œ

  • ํ•„์š” ๋ชจ๋“ˆ ์ž„ํฌํŠธ

    """\
    image_stitching.py
    module import part
    """
    import os
    import math
    from PIL import Image
    from imutils import paths
    import numpy as np
    import argparse
    import imutils
    import cv2
  • ์ด๋ฏธ์ง€ ๋ฆฌ์‚ฌ์ด์ฆˆ

    """\
    image_stitching.py
    function resize
    ํŒŒ์ผ ์ด๋ฆ„์„ ๋ฐ›์•„์„œ ์—ด๊ณ  ์ด๋ฏธ์ง€์˜ ํฌ๊ธฐ๊ฐ€ 1MB๋ณด๋‹ค ํฌ๋ฉด
    ํฌ๊ธฐ๋ฅผ ์ ์  ์ค„์—ฌ๋‚˜๊ฐ€์„œ 1MB๋ณด๋‹ค ์ž‘๊ฒŒ ๋˜๋ฉด ํ•ด๋‹น ์ด๋ฏธ์ง€๋ฅผ ๋ฆฌํ„ดํ•˜๋Š” ํ•จ์ˆ˜
    ์ฒ˜์Œ๋ถ€ํ„ฐ 1MB๋ณด๋‹ค ์ž‘๋‹ค๋ฉด ๊ทธ๋Œ€๋กœ ๋ฆฌํ„ด
    """
    def resize(filename):
        img = cv2.imread(filename)
        width, height = img.shape[:2]
        if height * width * 3 <= 2 ** 25:
            return img
        i = 2
        t_height, t_width = height, width
        while t_height * t_width * 3 > 2 ** 25:
            t_height = int(t_height / math.sqrt(i))
            t_width = int(t_width / math.sqrt(i))
            i += 1
        height, width = t_height, t_width
        image = Image.open(filename)
        resize_image = image.resize((height, width))
        filename = filename[:-1 * (len(filename.split(".")[-1]) + 1)] + "_resized." + filename.split(".")[-1]
        resize_image.save(filename)
        img = cv2.imread(filename)
        os.system("del " + filename.replace("/", "\\"))
        return img
  • ์ž…๋ ฅ ๊ฐ’ ์ฒ˜๋ฆฌ

    """\
    image_stitching.py
    argument parsing
    -i ์ž…๋ ฅ ์ด๋ฏธ์ง€๋“ค์˜ ํด๋” ๋””๋ ‰ํ† ๋ฆฌ
    -o ์ถœ๋ ฅํ•  ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€์˜ ํŒŒ์ผ ์ด๋ฆ„
    """
    ap = argparse.ArgumentParser()
    ap.add_argument("-i", "--images", type=str, required=True,
                    help="path to input directory of images to stitch")
    ap.add_argument("-o", "--output", type=str, required=True,
                    help="path to the output image")
    args = vars(ap.parse_args())
  • ์ด๋ฏธ์ง€ ๋กœ๋”ฉ

    """\
    image_stitching.py
    loading images
    ์ž…๋ ฅ ๋ฐ›์€ ํด๋”์˜ ๋ชจ๋“  ํŒŒ์ผ๋“ค์„ ๋ฆฌ์‚ฌ์ด์ฆˆ ์‹œํ‚ค๊ณ  ๋ฆฌ์ŠคํŠธ์— ๋กœ๋“œํ•˜๋Š” ๋ถ€๋ถ„
    """
    print("[INFO] loading images...")
    imagePaths = sorted(list(paths.list_images(args["images"])))
    images = []
    
    for imagePath in imagePaths:
        image = resize(imagePath)
        images.append(image)
  • Image Stitching

    """\
    image_stitching.py
    image stitching
    ์ด๋ฏธ์ง€ ์Šคํ‹ฐ์นญํ•˜์—ฌ ์„ฑ๊ณต ์—ฌ๋ถ€(status)์™€ ๊ฒฐ๊ณผ(stitched)๋ฅผ ๋ฐ›๋Š” ๋ถ€๋ถ„
    """
    print("[INFO] stitching images...")
    stitcher = cv2.createStitcher() if imutils.is_cv3() else cv2.Stitcher_create()
    (status, stitched) = stitcher.stitch(images)
  • ๊ฒฐ๊ณผ ์ €์žฅ ๋ฐ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ

    """\
    image_stitching.py
    exception handling
    status๊ฐ€ 0์ธ ๊ฒฝ์šฐ(stitching ์„ฑ๊ณต) ์ด๋ฏธ์ง€๋ฅผ ์•„๊นŒ ๋ฐ›์€ ์ด๋ฆ„์œผ๋กœ ์ €์žฅํ•˜๊ณ  ๋ณด์—ฌ์ค€๋‹ค.
    status๊ฐ€ 0์ด ์•„๋‹Œ ๊ฒฝ์šฐ(stitching ์‹คํŒจ ์˜ˆ์™ธ)์˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค.
        1 : ์ด๋ฏธ์ง€๋ฅผ ์—ฐ๊ฒฐ ์‹œํ‚ค๊ธฐ์— match point๊ฐ€ ๋ถ€์กฑํ•ด์„œ ๋‚˜์˜ค๋Š” ์—๋Ÿฌ, ์ด๋ฏธ์ง€๋ฅผ ๋” ์ถ”๊ฐ€์‹œ์ผœ์ค˜์•ผ ํ•œ๋‹ค.
        2 : 2D ์ด๋ฏธ์ง€ ๋ณ€ํ™˜์„ ํ•˜์ง€ ๋ชปํ•˜๋Š” ์—๋Ÿฌ, ์ด๋ฏธ์ง€๋ฅผ ๋‹ค์‹œ ์ฐ๋Š” ๊ฒƒ์„ ์ถ”์ฒœํ•œ๋‹ค.
        3 : ์นด๋ฉ”๋ผ ์œ„์น˜์˜ ์—๋Ÿฌ, ์นด๋ฉ”๋ผ์˜ ๋ฐฉํ–ฅ์ด ์ž˜๋ชป๋ผ์„œ ๋‚˜์˜ค๋Š” ์—๋Ÿฌ, ์ž…๋ ฅ ์ด๋ฏธ์ง€๋“ค์„ ๊ฐ™์€ ๋ฐฉํ–ฅ์œผ๋กœ ํšŒ์ „์‹œํ‚ค๊ฑฐ๋‚˜ ์ƒˆ๋กœ์šด ์ด๋ฏธ์ง€๋ฅผ ์ฐ์–ด์•ผ ํ•œ๋‹ค.
    """
    if status == 0:
        # write the output stitched image to disk
        cv2.imwrite(args["output"], stitched)
    
        # display the output stitched image to our screen
        cv2.imshow("Stitched", stitched)
        cv2.waitKey(0)
    else:
        if status == cv2.STITCHER_ERR_NEED_MORE_IMGS:
            print("[INFO] image stitching failed (1: STITCHER_ERR_NEED_MORE_IMGS)")
        elif status == cv2.STITCHER_ERR_HOMOGRAPHY_EST_FAIL:
            print("[INFO] image stitching failed (2: STITCHER_ERR_HOMOGRAPHY_EST_FAIL)")
        else:
            print("[INFO] image stitching failed (3: STITCHER_ERR_CAMERA_PARAMETERS_ADJUSTMENT_FAIL)")
    

๐Ÿ–จ๊ฒฐ๊ณผ

  • ์ž…๋ ฅ ์ด๋ฏธ์ง€

  • ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€

๐Ÿ“ˆ๊ฐœ์„  ์‚ฌํ•ญ

์œ„์™€ ๊ฐ™์ด ๊ฒน์น˜๋Š” ๋ถ€๋ถ„๋„ ๋งŽ๊ณ  ๋งค์นญ ํฌ์ธํŠธ๊ฐ€ ๋ช…ํ™•ํ•œ ์ด๋ฏธ์ง€๋Š” ์‰ฌ์šด๋ฐ ๊ฒฐ๊ณผ๊ฐ€ ์ž˜ ์•ˆ ๋‚˜์˜ค๋Š” ์ด๋ฏธ์ง€๋„ ๋งŽ์•˜๋‹ค.

  1. ํ˜ธ์ˆ˜ ์ด๋ฏธ์ง€๊ฐ€ ์–ด๋งˆ ๋ฌด์‹œํ•˜๊ฒŒ ์ปค์„œ ์ฃผ๋ณ€ ๋•… ๋ถ€๋ถ„์ด ์—†๊ณ  ๋ฌผ ๋ถ€๋ถ„๋ฐ–์— ์—†๋Š” ์ด๋ฏธ์ง€๋Š” ๋งค์น˜ ํฌ์ธํŠธ ์ž์ฒด๋ฅผ ๋ชป ์ฐพ์•˜๋‹ค
  2. ๊ฐ€๋” ์ด๋ฏธ์ง€๊ฐ€ ๋’ค์ง‘ํžŒ ๊ฒฝ์šฐ์—” ์ž˜ ๋ชป ๋ถ™์ด๋Š” ๊ฒฝ์šฐ๋„ ์žˆ๋‹ค.
  3. ์ด๋ฏธ์ง€์— ํŒจํ„ด์ด ์žˆ๋Š” ๊ฒฝ์šฐ๋Š” 2D ๋ณ€ํ™˜์„ ์‹œํ‚จ๊ฒŒ ํ‹ฐ๋‚˜๊ณ  ์—ฐ๊ฒฐ ํ–ˆ์„ ๋•Œ ๋๊ณผ ๋์ด ์ •ํ™•ํ•˜๊ฒŒ ์—ฐ๊ฒฐ์ด ์•ˆ ๋˜๋Š” ๊ฒฝ์šฐ๋„ ์žˆ๋‹ค. -> ์ด๊ฑด ์ˆ˜์ •ํ•˜๊ฒŒ ๋˜๋ฉด ๋„ˆ๋ฌด ์˜ค๋ฒ„ ํ”ผํŒ…์ด ๋  ๊ฒƒ ๊ฐ™๋‹ค.

๐Ÿ“ŠGraph Making

๐ŸŽฏ๋ชฉํ‘œ

  • ์ด๋ฏธ์ง€์˜ HSV ์ƒ‰์ƒ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋…น์กฐ ์œ„์น˜์™€ ์ •๋„๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๊ทธ๋ž˜ํ”„๋ฅผ ๋งŒ๋“ ๋‹ค.

๐Ÿคทโ€โ™‚๏ธ๋ฌธ์ œ ํ•ด๊ฒฐ ์ˆœ์„œ

  • ์ด๋ฏธ์ง€ ๋กœ๋“œ
    • ์ด๋ฏธ์ง€ ๋ฆฌ์‚ฌ์ด์ฆˆ
  • ์ด๋ฏธ์ง€์˜ HSV ๊ฐ’ ์ถ”์ถœ
  • HSV ๊ฐ’์—์„œ ๋…น์กฐ ๋ ˆ๋ฒจ ๊ณ„์‚ฐ
  • ๊ณ„์‚ฐ๋œ ๊ฐ’์„ ๊ทธ๋ž˜ํ”„๋กœ ๋งŒ๋“ค๊ธฐ

๐Ÿค”๋ฌธ์ œ ํ•ด๊ฒฐ

  • HSV ๊ฐ’ ์ถ”์ถœ

    • HSV

      • HSV ๋ชจ๋ธ์€ ์ธ๊ฐ„์˜ ์ƒ‰์ธ์ง€์— ๊ธฐ๋ฐ˜์„ ๋‘” ์ƒ‰์ƒ ๋ชจ๋ธ
      • Hue(์ƒ‰์กฐ), Saturation(์ฑ„๋„), Value(๋ช…๋„), 3๊ฐ€์ง€ ์กฐํ•ฉ์œผ๋กœ ํ‘œํ˜„
      • Hue(์ƒ‰์กฐ) : ์ƒ‰์˜ ์ข…๋ฅ˜. 0ยบ~360ยบ์˜ ๋ฒ”์œ„๋ฅผ ๊ฐ–๋Š”๋‹ค.
      • Saturation(์ฑ„๋„) : ์ƒ‰์˜ ์„ ๋ช…๋„, ์ง„ํ•จ์˜ ์ •๋„(๊ฐ€์žฅ ์ง„ํ•œ ์ƒํƒœ๋ฅผ 100%๋กœ ํ•œ๋‹ค)
      • Value(๋ช…๋„) : ์ƒ‰์˜ ๋ฐ๊ธฐ, ๋ฐ์€ ์ •๋„(๊ฐ€์žฅ ๋ฐ์€ ์ƒํƒœ๋ฅผ 100%๋กœ ํ•œ๋‹ค)
    • OpenCv์˜ BGR2HSV ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์“ฐ๋ฉด ๋จ

  • ๋…น์กฐ ๋ ˆ๋ฒจ ๊ณ„์‚ฐ

    • ํด๋ผ์ด์–ธํŠธ์—์„œ ์ค€ ๋…น์กฐ ๊ณ„์‚ฐ ๋ฐฉ๋ฒ•์„ ๊ฐ–๊ณ  ๋…น์กฐ ๋ ˆ๋ฒจ์„ 0๋ถ€ํ„ฐ 3์œผ๋กœ ๊ตฌ๋ถ„ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.

      def set_range(h, s, v):
          if 72 >= h >= 66 and 91 >= s >= 59 and 255 >= v >= 66:
              return 3
          elif 75 >= h >= 73 and 234 >= s >= 31 and 118 >= v >= 0:
              return 2
          elif 134 >= h >= 35 and 59 >= s >= 0 and 100 >= v >= 73:
              return 1
          return 0
  • ๊ณ„์‚ฐ๋œ ๊ฐ’์œผ๋กœ ๊ทธ๋ž˜ํ”„ ๋งŒ๋“ค๊ธฐ

    • Plotly ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ graph_object๋กœ HeatMap๊ณผ 3DGraph๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.

โŒจ์ฝ”๋“œ

  • ํ•„์š” ๋ชจ๋“ˆ ์ž„ํฌํŠธ

    """\
    graph_maker.py
    ํ•„์š”ํ•œ ๋ชจ๋“ˆ ์ž„ํฌํŠธ
    """
    import argparse
    import os
    import cv2
    import math
    import numpy as np
    import pandas as pd
    import plotly.graph_objects as go
    import plotly
    from PIL import Image
  • ๋…น์กฐ ๋ ˆ๋ฒจ ๊ณ„์‚ฐ ํ•จ์ˆ˜

    """\
    graph_maker.py
    HSV ๋ฐ์ดํ„ฐ๋กœ ๋…น์กฐ ๋ ˆ๋ฒจ ๊ณ„์‚ฐํ•˜๋Š” ํ•จ์ˆ˜
    """
    def set_range(h, s, v):
        if 72 >= h >= 66 and 91 >= s >= 59 and 255 >= v >= 66:
            return 3
        elif 75 >= h >= 73 and 234 >= s >= 31 and 118 >= v >= 0:
            return 2
        elif 134 >= h >= 35 and 59 >= s >= 0 and 100 >= v >= 73:
            return 1
        return 0
  • ๊ทธ๋ž˜ํ”„์˜ ์œ„๊ฒฝ๋„ ๊ฐ’ ๊ณ„์‚ฐ

    """\
    graph_maker.py
    ์œ„๊ฒฝ๋„๋ฅผ ๊ทธ๋ž˜ํ”„์˜ x, y์ถ•์— ํ‘œ๊ธฐํ•˜๊ธฐ ์œ„ํ•ด ์œ„๊ฒฝ๋„ ๊ฐ’์„ ์ด๋ฏธ์ง€์˜ ๋„ˆ๋น„/๋†’์ด์˜ ํ”ฝ์…€ ๊ฐœ์ˆ˜๋งŒํผ์œผ๋กœ ๋‚˜๋ˆ ์„œ ๋ฆฌ์ŠคํŠธ๋กœ ๋ฆฌํ„ดํ•˜๋Š” ํ•จ์ˆ˜
    ํŒŒ์ด์ฌ range๋กœ๋Š” float ํƒ€์ž… ๋ฐ์ดํ„ฐ๋ฅผ ๋‚˜๋ˆ„์ง€ ๋ชป ํ•˜๋ฏ€๋กœ ๋”ฐ๋กœ ์ œ์ž‘
    """
    def float_range(start, end, cnt):
        list = []
        step = (end - start) / (cnt - 1)
        for i in range(cnt):
            list.append(start + i * step)
        return list
  • ์ž…๋ ฅ๊ฐ’ ์ฒ˜๋ฆฌ

    """\
    graph_maker.py
    argument parsing
    -i ๊ทธ๋ž˜ํ”„๋กœ ๋งŒ๋“ค ์ด๋ฏธ์ง€ ํŒŒ์ผ ์ด๋ฆ„
    -o ์ถœ๋ ฅ์œผ๋กœ ์ €์žฅํ•  ์ด๋ฏธ์ง€ ํŒŒ์ผ ์ด๋ฆ„
    -lat1 ์œ„๋„์˜ ์‹œ์ž‘ ๊ฐ’
    -lat2 ์œ„๋„์˜ ๋ ๊ฐ’
    -lon1 ๊ฒฝ๋„์˜ ์‹œ์ž‘ ๊ฐ’
    -lon2 ๊ฒฝ๋„์˜ ๋ ๊ฐ’
    lat1๊ณผ lat2๋Š” ๋‘˜ ๋‹ค ์—†๊ฑฐ๋‚˜ ๋‘˜ ๋‹ค ์žˆ๊ฑฐ๋‚˜ ํ•ด์•ผํ•จ
    lon1๊ณผ lon2๋Š” ๋‘˜ ๋‹ค ์—†๊ฑฐ๋‚˜ ๋‘˜ ๋‹ค ์žˆ๊ฑฐ๋‚˜ ํ•ด์•ผํ•จ
    """
    ap = argparse.ArgumentParser()
    ap.add_argument("-i", "--image", type=str, required=True,
                    help="path to input file name of images to stitch")
    ap.add_argument("-o", "--output", type=str, required=True,
                    help="path to the output image")
    ap.add_argument("-lat1", "--latitude1", type=float, required=False,
                    help="image's start latitude")
    ap.add_argument("-lat2", "--latitude2", type=float, required=False,
                    help="image's end latitude")
    ap.add_argument("-lon1", "--longitude1", type=float, required=False,
                    help="image's start longitude")
    ap.add_argument("-lon2", "--longitude2", type=float, required=False,
                    help="image's end longitude")
    args = vars(ap.parse_args())
  • ์ด๋ฏธ์ง€ ๋กœ๋”ฉ

    """\
    graph_maker.py
    ์ด๋ฏธ์ง€๊ฐ€ 1MB๋ณด๋‹ค ํฌ๋ฉด ์ค„์ด๊ณ  ์ž‘์œผ๋ฉด ๊ทธ๋Œ€๋กœ ๋กœ๋“œํ•˜๋Š” ๋ถ€๋ถ„
    """
    print("Image loading start")
    print("Image resizing start")
    img_color = resize(filename)
    height, width = img_color.shape[:2]
    print("Image resizing end")
    print("Image loading end")
  • HSV ๊ฐ’ ์ถ”์ถœ

    """\
    graph_maker.py
    ์ด๋ฏธ์ง€๋ฅผ ๋กœ๋“œํ•˜๋ฉด ์ฒ˜์Œ์—” ๊ฐ ํ”ฝ์…€์˜ BGR(RGB)๊ฐ’์„ ๊ฐ€์ง€๋Š” numpy array๋กœ ์ €์žฅ๋˜์–ด ์žˆ๋‹ค.
    ์ด๋ฏธ์ง€๋ฅผ HSVํ˜•์‹์œผ๋กœ ๋ณ€ํ˜•์‹œํ‚ค๊ณ  H, S, V ๊ฐ’์„ ๊ฐ๊ฐ column์œผ๋กœ ๊ฐ€์ง€๋Š” pandas DataFrame์œผ๋กœ ๋ณ€ํ˜• ์‹œํ‚จ๋‹ค.
    """
    img_hsv = cv2.cvtColor(img_color, cv2.COLOR_BGR2HSV)
    print("Extracting HSV data start")
    data = pd.DataFrame(np.concatenate(img_hsv))
    print("Extracting HSV data end")
    data.columns = ["H", "S", "V"]
  • ๋…น์กฐ ๋ ˆ๋ฒจ ๊ณ„์‚ฐ

    """\
    graph_maker.py
    DataFrame์œผ๋กœ ๋ณ€ํ˜•์‹œํ‚จ ๋ฐ์ดํ„ฐ๋ฅผ applyํ•จ์ˆ˜(๊ฐ ์ธ๋ฑ์Šค์— ์ž…๋ ฅ ๋ฐ›๋Š” ํ•จ์ˆ˜๋ฅผ ์ ์šฉ)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์•„๊นŒ ๋งŒ๋“  set_range()๋ฅผ ๋ชจ๋“  ํ”ฝ์…€์— ์ ์šฉ์‹œํ‚จ๋‹ค.
    ๊ทธ๋Ÿฌ๊ณ  ์ถœ๋ ฅํ•ด๋ณด๋‹ˆ ์ด๋ฏธ์ง€์™€ ๊ทธ๋ž˜ํ”„๊ฐ€ ๊ฑฐ๊พธ๋กœ ๋‚˜์˜ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•ด์„œ ๋’ค์ง‘์–ด์คฌ๋‹ค.(flip())
    """
    print("Calculating \"Range\" data start")
    data["Range"] = data.apply(lambda x: set_range(x['H'], x['S'], x['V']), axis=1)
    data["Range"] = np.flip(data["Range"])
    print("Calculating \"Range\" data end")
  • ๋…น์กฐ ๋ ˆ๋ฒจ ๋ฐ์ดํ„ฐ๋ฅผ ์ด๋ฏธ์ง€ ๋ชจ์–‘์œผ๋กœ ๋งŒ๋“ค๊ธฐ

    """\
    graph_maker.py
    ํ˜„์žฌ๋Š” column ํ•˜๋‚˜์งœ๋ฆฌ DataFrame์œผ๋กœ ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ๊ทธ๋ž˜ํ”„๋กœ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ๋†’์ด * ๋„ˆ๋น„ ํฌ๊ธฐ์˜ DataFrame์œผ๋กœ ๋งŒ๋“ค์–ด ์ค€๋‹ค.
    ๊ทธ๋ฆฌ๊ณ  ์•„๊นŒ ์œ„๋„๋‚˜ ๊ฒฝ๋„๋ฅผ ๋ฐ›์•˜์œผ๋ฉด x, y์ถ•์— ๋งคํ•‘ํ•ด์ค€๋‹ค.
    """
    y = np.array(data['Range'])
    z_data = pd.DataFrame(y.reshape(height, width))  # Size variations according to pixel
    if args['longitude1']:
        z_data.index = float_range(args['longitude1'], args['longitude2'], height)
    if args['latitude1']:
        z_data.columns = float_range(args['latitude1'], args['latitude2'], width)
  • HeatMap ๋งŒ๋“ค๊ธฐ

    """\
    graph_maker.py
    plotly์˜ graph object๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ HeatMap์„ ๋งŒ๋“ ๋‹ค
    ์ƒ‰์ƒ์€ ํด๋ผ์ด์–ธํŠธ์—์„œ ์ค€ ์ปค์Šคํ…€ ์ƒ‰์ƒ์œผ๋กœ ํ•˜๊ณ  ํƒ€์ดํ‹€๊ณผ ๋งˆ์ง„์„ ์ค€๋‹ค.
    """
    fig_heatmap = go.Figure(data=go.Heatmap(z=z_data,
                                            connectgaps=True,
                                            colorscale=[[0.0, "rgb(0,8,135)"],
                                                        [1.0, "rgb(0,135,8)"]]))
    fig_heatmap.update_layout(title='HSV Graph', autosize=True,
                              margin=dict(l=65, r=50, b=65, t=90))
  • 3D ๊ทธ๋ž˜ํ”„ ๋งŒ๋“ค๊ธฐ

    """\
    graph_maker.py
    plotly์˜ graph object๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ 3D Graph๋ฅผ ๋งŒ๋“ ๋‹ค
    ์ƒ‰์ƒ์€ ํด๋ผ์ด์–ธํŠธ์—์„œ ์ค€ ์ปค์Šคํ…€ ์ƒ‰์ƒ์œผ๋กœ ํ•˜๊ณ  ํƒ€์ดํ‹€๊ณผ ๋งˆ์ง„์„ ์ค€๋‹ค.
    """
    fig_3d = go.Figure(data=[go.Surface(z=z_data.values,
                                        colorscale=[[0.0, "rgb(0,8,135)"],
                                                    [1.0, "rgb(0,135,8)"]])])
    fig_3d.update_layout(title='HSV Graph', autosize=True,
                         scene_camera_eye=dict(x=1.87, y=0.88, z=-0.64),
                         margin=dict(l=65, r=50, b=65, t=90))
  • ์ด๋ฏธ์ง€ ์ €์žฅ ํ›„ ํ‘œ์‹œ

    """\
    graph_maker.py
    ์‹คํ–‰ ์‹œ์— ์ž…๋ ฅ ๋ฐ›์€ ์ด๋ฆ„์— ๊ฐ๊ฐ Heatmap๊ณผ 3D_graph ์ด๋ฆ„์„ ๋ถ™์—ฌ์„œ ์ด๋ฏธ์ง€๋กœ ์ €์žฅํ•˜๊ณ 
    ์›น ๋ธŒ๋ผ์šฐ์ €๋กœ ์ธํ„ฐ๋ ‰ํ‹ฐ๋ธŒํ•œ js ๊ทธ๋ž˜ํ”„๋ฅผ ๋„์–ด์ค€๋‹ค.
    """
    output = args['output']
    if '.' in args['output']:
        output = args['output'][:-1 * (len(args['output'].split(".")[-1]) + 1)]
    else:
        args['output'] = output + ".png"
    plotly.io.write_image(fig_heatmap, output + "_heatmap." + args['output'].split(".")[-1])
    plotly.io.write_image(fig_3d, output + "_3D_graph." + args['output'].split(".")[-1])
    fig_heatmap.show()
    fig_3d.show()

๐Ÿ–จ๊ฒฐ๊ณผ

  • ์ž…๋ ฅ ์ด๋ฏธ์ง€

  • ๊ฒฐ๊ณผ ๊ทธ๋ž˜ํ”„

    • HeatMap

    • 3D Graph

๐Ÿ“ˆ๊ฐœ์„  ์‚ฌํ•ญ

  • ์›๋ณธ ์ด๋ฏธ์ง€, ๋…น์กฐ ์œ„์น˜๋ฅผ ํ‘œ์‹œํ•ด๋‘” ์ด๋ฏธ์ง€ ๋ฐ์ดํ„ฐ์…‹ ์ž…๋ ฅ์„ ์ค˜์„œ ๋จธ์‹ ๋Ÿฌ๋‹์„ ์‹œํ‚ฌ ์ˆ˜๋„ ์žˆ๊ฒ ๋‹ค.
  • ๋‚ด ๋„๋ฉ”์ธ ์ง€์‹์ด ์•„๋‹ˆ๋ผ ์ •ํ™•ํ•˜๊ฒŒ ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ ๋๋‚  ๋•Œ ์ฏค ๋“  ์ƒ๊ฐ์ธ๋ฐ, ํ™˜๊ฒฝ(ํ•ด์˜ ์œ„์น˜, ๊ตฌ๋ฆ„, ๊ณ„์ ˆ ๋“ฑ)์— ๋”ฐ๋ผ ์ด๋ฏธ์ง€์˜ ์ƒ‰์ƒ์ด ๋‹ฌ๋ผ์ ธ์„œ ๋…น์กฐ ๋ฐ์ดํ„ฐ ๊ฒ€์ถœ ์ž์ฒด๊ฐ€ ๊ฐ€๋Šฅํ• ์ง€?์— ๋Œ€ํ•œ ์˜๋ฌธ์ด ๋“ ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ด๋ฏธ์ง€ ์ƒ‰์ƒ์„ ์ •๊ทœํ™”์‹œ์ผœ์•ผ ํ•  ๋“ฏ ํ•˜๋‹ค.

โฒํšŒ๊ณ 

๐Ÿ‘์ž˜ํ•œ ์ 

  • ๐Ÿ“…์ค‘์žฅ๊ธฐ ํ”„๋กœ์ ํŠธ ์„ฑ๊ณต!
    ์ง€๊ธˆ๊นŒ์ง€๋Š” ์ค‘์žฅ๊ธฐ์ (3 ~ 4๊ฐœ์›”) ํ”„๋กœ์ ํŠธ๋ฅผ ๋ณ‘ํ–‰ํ•˜์—ฌ ์ง„ํ–‰ํ•˜๊ธฐ ํž˜๋“ค์–ด ํ•˜๊ณ  ํ•œ ๋ฒˆ์— ํ›„๋”ฑ ๋๋‚ด๋ฒ„๋ฆฌ๋Š” ๊ฒƒ์„ ์ข‹์•„ํ•ด์„œ ๊ทธ๋ ‡๊ฒŒ ํ•ด์™”๋‹ค. ์ด๋ฒˆ ํ•™๊ธฐ๋Š” ์—ฌ๋Ÿฌ ํ”„๋กœ์ ํŠธ, ์ˆ˜์—…, ๊ณผ์ œ, ํ•™์Šต, ๋Œ€์™ธ ํ™œ๋™์ด ๊ฒน์ณค์ง€๋งŒ ์ž˜ ์ง„ํ–‰ํ–ˆ๋‹ค!
  • ๐Ÿ“ท์˜์ƒ ์ฒ˜๋ฆฌ ํ”„๋กœ์ ํŠธ ๊ฒฝํ—˜ ํš๋“
    ์ด์ „์— ์˜์ƒ ์ฒ˜๋ฆฌ ๊ด€๋ จ ํ”„๋กœ์ ํŠธ๋ฅผ ํ•ด ๋ณธ ์ ์ด ์—†์–ด์„œ ์ฒ˜์Œ ์ฃผ์ œ๋ฅผ ๋ฐ›์•˜์„ ๋•Œ๋Š” "๋‚ด๊ฐ€ ์ด๊ฑธ ํ•  ์ˆ˜ ์žˆ์„๊นŒ?"๋ž€ ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋ฌธ์ œ๋ฅผ ๋ช…ํ™•ํ•˜๊ฒŒ ํŒŒ์•…ํ•˜๊ณ  ๋‹จ๊ณ„๋ฅผ ๋‚˜๋ˆ„๊ณ  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ž˜ ์ฐพ์•„๋ณด๋‹ˆ ์˜์™ธ๋กœ ์ž˜ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. ์ด๋ฒˆ์— ๋˜ ๋‹ค์‹œ ๋Š๋‚€ ๊ฒƒ์€ ๋‚ด๊ฐ€ ๊ถ๊ธˆํ•ด ํ•˜๊ณ  ์ฝ”๋”ฉ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ๋Š” ์ด๋ฏธ ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ํ•ด๊ฒฐํ•˜๊ณ  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(ํ˜น์€ ๋…ผ๋ฌธ)์œผ๋กœ ๋งŒ๋“ค์–ด ๋†จ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. (๊ตฌ๊ธ€๋ง, ๋ธ”๋กœ๊ทธ, ์Šคํƒ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ ์ตœ๊ณ )

๐Ÿ˜ฅ์•„์‰ฌ์šด ์ 

  • ๐Ÿ“š๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ธ๋ถ€ ์„ค์ • ์‹คํŒจ
    ์ฒ˜์Œ์—๋Š” OpenCV Stitcher ํด๋ž˜์Šค ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ์„ธ๋ถ€ ์„ค์ •์„ ํ•˜๋ ค๊ณ  ํ–ˆ3๋‹ค. ๊ทธ๋Ÿฐ๋ฐ, Stitcher ํด๋ž˜์Šค๋Š” ์ž์„ธํžˆ ์„ค๋ช…ํ•ด ๋‘” ์ •๋ณด๊ฐ€ ์—†์—ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  github ๋ ˆํฌ์— ๊ฐ€๋ด๋„ ์˜ˆ์‹œ ์ฝ”๋“œ์— ์„ธ๋ถ€ ์„ค์ •์„ ์–ด๋–ป๊ฒŒ ํ•˜๋Š”์ง€ ์ž˜ ๋‚˜์™€์žˆ์ง€ ์•Š์•˜๋‹ค.... ๊ทธ๋ž˜์„œ ๋ฌธ์ œ๋ฅผ ๋‹จ๊ณ„๋ณ„๋กœ ์ง์ ‘ ์ฝ”๋”ฉํ•ด์„œ ํ•ด๊ฒฐํ•ด ๋ณด๋ ค๊ณ  ํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ 2D ๋ณ€๊ฒฝ์„ ํ•˜๋‹ˆ๊นŒ ํ™”์งˆ์ด ๋„ˆ๋ฌด ๊นจ์ง€๊ณ , ์ด๋ฏธ์ง€ ์ž…๋ ฅ ์ˆœ์„œ์— ๋”ฐ๋ผ ์ด๋ฏธ์ง€๊ฐ€ ์งค๋ฆฌ๋Š” ์ด์Šˆ๊ฐ€ ์žˆ์–ด์„œ, ๊ฒฐ๊ตญ ๊ธฐ๊ฐ„ ๋‚ด์— ์ตœ์ข… ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜์ง€ ๋ชป ํ•  ๊ฒƒ ๊ฐ™์•„์„œ ํฌ๊ธฐํ•˜๊ณ  ๊ธฐ๋ณธ ์˜ต์…˜์œผ๋กœ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋๋ƒˆ๋‹ค. ์˜์ƒ ์ฒ˜๋ฆฌ์— ๋Œ€ํ•œ ์ง€์‹์ด ์ข€ ๋” ์žˆ๊ณ  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฌธ์„œ๊ฐ€ ์ž˜ ๋ผ ์žˆ์—ˆ์œผ๋ฉด ํ•  ์ˆ˜ ์žˆ์—ˆ์„ ๊ฒƒ ๊ฐ™์•„์„œ ์•„์‰ฝ๋‹ค.
  • โ˜ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋ถ€์กฑ
    ์ž…๋ ฅ ๊ฐ’์— ๋Œ€ํ•œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๊ฐ€ ์ข€ ๋ถ€์กฑํ–ˆ๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค. ์ด๋ฏธ์ง€๊ฐ€ ๋„ˆ๋ฌด ๋งŽ๊ฑฐ๋‚˜ ์ปค์„œ ๋‚˜์˜ค๋Š” ์˜ค๋ฅ˜๋Š” ์ด๋ฏธ์ง€ ๋ฆฌ์‚ฌ์ด์ง•์„ ํ†ตํ•ด ํ•ด๊ฒฐํ–ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ํŒŒ์ผ์ด ์—†๊ฑฐ๋‚˜, ๋™์ผ ์ด๋ฆ„์˜ ํŒŒ์ผ์ด ์žˆ๋Š” ๊ฒฝ์šฐ์— ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋ฅผ ์ž˜ ๋ชปํ•˜๊ณ  ๋๋‚ฌ๋‹ค๋Š” ์•„์‰ฌ์›€์ด ๋‚จ๋Š”๋‹ค.

๐Ÿ“ธ๋ฐœํ‘œ ์‚ฌ์ง„

profile
๐Ÿ’ช ๐Ÿฅฉ ๐Ÿบ โœˆ ๐Ÿ’ป

0๊ฐœ์˜ ๋Œ“๊ธ€