<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>FoO의 개발 블로그</title>
    <link>https://foo-511.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Wed, 15 Apr 2026 12:31:19 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>FoO__511</managingEditor>
    <image>
      <title>FoO의 개발 블로그</title>
      <url>https://tistory1.daumcdn.net/tistory/5292608/attach/26c53f150a64499b8e5e95406cfe47ea</url>
      <link>https://foo-511.tistory.com</link>
    </image>
    <item>
      <title>[OpenCV] OpenCV-Python으로 배우는 영상 처리 및 응용 - 6장 연습문제 풀이</title>
      <link>https://foo-511.tistory.com/21</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 그레이 스케일(gray-scale) 이미지가 의미하는 것이 무엇인지 설명하시오.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디지털 영상처리에서 보통 단일채널의 영상을 그레이 스케일(gray-scale) 영상이라고 부른다. 하나의 화소값은 0~255의 값을 가지는데 0은 검은색을, 255는 흰색을 의미한다. 이렇게 화소값이 회색의 비율 정도로 표현되고, 0~255값을 가지는 화소들이 모여서 구성된 것이 영상이 때문에 그레이 스케일 영상이라 한다.&lt;br /&gt;&lt;br /&gt;(교재 226p)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 화소의 밝기와 화소값에 대해서 설명하시오.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 화소값은 0~255의 값을 가지는데 0은 검은색을, 255는 흰색을 의미한다.&lt;br /&gt;(교재 226p)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 두 개의 영상을 합성하는 방법을 두 가지 이상 기술하시오.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 개의 영상을 합하면 영상 합성을 할 수 있다.&lt;br /&gt;OpenCV를 이용하여 영상 합성을 할 수 있는 방법은 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1751352486102&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;add_img1 = cv2.add(image1 , image2) # 두 영상 단순 더하기
alpha, beta = 0.6, 0.7

#####

add_img2 = cv2.add(image1 * alpha , image2 * beta) # 두영상 비율에 따른 더하기
add_img2 = np.clip(add_img2, 0, 255).astype(&quot;uint8&quot;) # saturation 처리

#####

alpha, beta = 0.6, 0.7
add_img3 = cv2.addWeighted(image1, alpha, image2, beta, 0)     # 두영상 비율에 따른 더하기&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 영상에서 밝기 변경과 명암 대비 변경의 차이를 설명하시오.&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.814%;&quot;&gt;밝기 변경&lt;/td&gt;
&lt;td style=&quot;width: 74.186%;&quot;&gt;화소값은 영상의 밝기를 나타내기 때문에 단순히 특정 상숫값을 더하면 영상이 밝아지고, 빼면 영상이 어두워진다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.814%;&quot;&gt;명암 대비 변경&lt;/td&gt;
&lt;td style=&quot;width: 74.186%;&quot;&gt;명암 대비(contrast)는 상이한 두 가지 색(밝기)이 경계에서 서로 영향을 미쳐 그 차이가 강조되어 나타나는 현상이다. 낮은 명암 대비의 영상은 어두운 값과 밝은 값의 차이가 크지 않아 전체적으로 어둡거나 밝은 영상이며, 높은 명암 대비 영상은 그와 반대이다.&lt;br /&gt;&lt;br /&gt;명암 대비 변경을 하기 위해서는 어두운 부분은 더 어둡게, 밝은 부분을 더 밝게 해야 하며, 단순 상숫값을 더하는 밝기 변경과는 차이가 있다.&lt;br /&gt;&lt;br /&gt;명암 대비 변경을 하는 가장 단순한 방법으로 곱셈 연산이 있다. 명암 대비를 늘리기 위해서는 1.0 이상의 값을 곱해주고, 명암 대비를 줄이기 위해서는 1.0 이하의 값을 곱해주면 된다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 영상처리에서 히스토그램이란 무엇인가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 밝기 부분만 있는 영상은 전체적으로 선명하지 않으며 또렷하지 않다. 이런 영상을 히스토그램으로 나타내면 한쪽으로 치우쳐서 히스토그램 분포가 좁은 그래프들로 나타난다. 히스토그램 분포가 좁아 영상 대비가 좋지 않은 영상들의 화질을 개선할 수 있는 알고리즘이 히스토그램 스트레칭(histogram stretching)이다.&lt;br /&gt;&lt;br /&gt;스트레칭이라는 용어에서 알 수 있듯이 명암 분포가 좁은 히스토그램을 좌우로 잡아당겨 고른 명암 분포를 가진 히스토그램이 되게 하는 것이다.&lt;br /&gt;&lt;br /&gt;(교재 246p)&lt;br /&gt;&lt;br /&gt;히스토그램의 x축 위에서 빈도값이 존재하는 가장 낮은 화소값(low value)과 가장 높은 화소값(high value)을 찾아 가장 낮은 화소값을 0으로 당기고 가장 높은 화소값을 255로 당긴다. 중간의 화소값들은 각각의 비율에 따라서 화소값의 위치를 조정하면 된다.&lt;br /&gt;&lt;br /&gt;실제 화소값에 대한 수식으로 표현하면 다음과 같다. 영상의 모든 화소에 대해서 다음 수식을 적용하면 히스토그램 스트레칭이 완료된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ 새 화소값 = \frac{(화소값) - low}{high - low} * 255 $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(교재 247p)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. 히스토그램 평활화 과정을 기술하시오.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;히스토그램 평활화를 통해 영상의 인지도를 높이며, 영상의 화질을 개선할 수 있다. 평활화 알고리즘은 히스토그램 평활화의 사전적 의미인 &amp;ldquo;분포의 균등&amp;rdquo;이라는 방법을 이용해 명암 대비를 증가시킨다. 히스토그램의 분포가 좁지는 않지만, 특정 부분에서 한쪽으로 치우친 명암 분포를 가진 영상들의 히스토그램 재분배 과정을 거쳐 균등한 히스토그램 분포를 가지게 하는 알고리즘이 &amp;ldquo;히스토그램 평활화&amp;rdquo; 알고리즘이다.&lt;br /&gt;&lt;br /&gt;히스토그램 평활화를 수행하는 전체 과정은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;영상의 히스토그램을 계산한다.&lt;/li&gt;
&lt;li&gt;히스토그램 빈도값에서 누적 빈도수(누적합)을 계산한다.&lt;/li&gt;
&lt;li&gt;누적 빈도수를 정규화(정규화 누적합) 한다.&lt;/li&gt;
&lt;li&gt;결과 화소값 = 정규화 누적합 * 최대 화소값&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(교재 252p)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8. OpenCV함수 중에서 cv2.addWeighted() 함수를 사용해서 두 영상을 합성하는 프로그램을 작성하시오.&lt;/h3&gt;
&lt;pre id=&quot;code_1751352692270&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import numpy as np, cv2

image1 = cv2.imread(&quot;images/add1.jpg&quot;, cv2.IMREAD_GRAYSCALE)
image2 = cv2.imread(&quot;images/add2.jpg&quot;, cv2.IMREAD_GRAYSCALE)
if image1 is None or image2 is None: raise Exception(&quot;영상 파일 읽기 오류 발생&quot;)

dst = cv2.addWeighted(image1, 0.5, image2, 0.5, None)
dst = np.hstack((image1,dst, image2))

cv2.imshow(&quot;dst&quot;, dst)
cv2.waitKey(0)
cv2.destroyAllWindows()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;352&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJAxv7/btsOZDIgW49/rzb8TLBkmVBdcJyQNkHztK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJAxv7/btsOZDIgW49/rzb8TLBkmVBdcJyQNkHztK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJAxv7/btsOZDIgW49/rzb8TLBkmVBdcJyQNkHztK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJAxv7%2FbtsOZDIgW49%2Frzb8TLBkmVBdcJyQNkHztK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;352&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;352&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9. 8번 문제에 두 개의 트렉바를 추가해서 각 영상의 반영 비율을 조절할 수 있도록 수정하시오.&lt;/h3&gt;
&lt;pre id=&quot;code_1751352774374&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import numpy as np, cv2

image1 = cv2.imread(&quot;images/add1.jpg&quot;, cv2.IMREAD_GRAYSCALE)
image2 = cv2.imread(&quot;images/add2.jpg&quot;, cv2.IMREAD_GRAYSCALE)
if image1 is None or image2 is None: raise Exception(&quot;영상 파일 읽기 오류 발생&quot;)

alpha, beta = 50, 50

def showWeightedImg():
    global alpha, beta
    dst = cv2.addWeighted(image1, alpha/100, image2, beta/100, None)
    dst = np.hstack((image1, dst, image2))
    cv2.imshow(&quot;dst&quot;, dst)

def onAlphaChange(a):
    global alpha
    alpha = a
    showWeightedImg()

def onBetaChange(b):
    global beta
    beta = b
    showWeightedImg()

cv2.namedWindow(&quot;dst&quot;)
cv2.createTrackbar(&quot;image1&quot;, &quot;dst&quot;, alpha, 100, onAlphaChange)
cv2.createTrackbar(&quot;image2&quot;, &quot;dst&quot;, beta, 100, onBetaChange)

showWeightedImg()
cv2.waitKey(0)
cv2.destroyAllWindows()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RuVVG/btsOYS0HsS2/WUnKTZWUzppd2Du9OtTL51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RuVVG/btsOYS0HsS2/WUnKTZWUzppd2Du9OtTL51/img.png&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;433&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.6082%; margin-right: 10px;&quot; data-widthpercent=&quot;33.38&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RuVVG/btsOYS0HsS2/WUnKTZWUzppd2Du9OtTL51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRuVVG%2FbtsOYS0HsS2%2FWUnKTZWUzppd2Du9OtTL51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;433&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y857F/btsOY6EjU3N/viV5BE0K5sKDEkTKSZc9Uk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y857F/btsOY6EjU3N/viV5BE0K5sKDEkTKSZc9Uk/img.png&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;434&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.5331%; margin-right: 10px;&quot; data-widthpercent=&quot;33.31&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y857F/btsOY6EjU3N/viV5BE0K5sKDEkTKSZc9Uk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy857F%2FbtsOY6EjU3N%2FviV5BE0K5sKDEkTKSZc9Uk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;434&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dpF6FC/btsO0Nb230n/klcJB7XEIqmZ95KMaqEJk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dpF6FC/btsO0Nb230n/klcJB7XEIqmZ95KMaqEJk1/img.png&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;434&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.5331%;&quot; data-widthpercent=&quot;33.31&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dpF6FC/btsO0Nb230n/klcJB7XEIqmZ95KMaqEJk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdpF6FC%2FbtsO0Nb230n%2FklcJB7XEIqmZ95KMaqEJk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;434&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;10. 예제_6.2.1를 수정해서 np.float32(CV_32F)형으로 행렬을 선언하며, 회색이 점진적으로 짙어지는 영상을 만드시오.&lt;/h3&gt;
&lt;pre id=&quot;code_1751352838318&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import numpy as np, cv2

image1 = np.ones((50, 512), np.float32)         # 50 x 512 영상 생성
image2 = np.ones((50, 512), np.float32)

rows, cols = image1.shape[:2]

for i in range(rows):                           # 행렬 전체 조회
    for j in range(cols):
        image1[i, j]= -1 - j // 2                # 화소값 점진적 증가
        image2[i, j]= -1 - j // 20 * 10          # 계단 현상 증가

cv2.imshow(&quot;image1&quot;, image1.astype('uint8'))
cv2.imshow(&quot;image2&quot;, image2.astype('uint8'))
cv2.waitKey(0)
cv2.destroyAllWindows()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;255&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYCvNu/btsO0Dnb3k2/zoK3tCLrP5Jm50gNkjwLF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYCvNu/btsO0Dnb3k2/zoK3tCLrP5Jm50gNkjwLF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYCvNu/btsO0Dnb3k2/zoK3tCLrP5Jm50gNkjwLF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYCvNu%2FbtsO0Dnb3k2%2FzoK3tCLrP5Jm50gNkjwLF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;255&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;255&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;11. cv2.calcHist() 함수는 1~3채널 영상에 대해서 히스토그램을 계산할 수 있다. 예제_6.3.5에서 calc_histo() 함수는 단일채널 영상에서 히스토그램을 계산하는데 cv2.calcHist() 함수와 같이 2채널 혹은 3채널 영상에서 2차원 혹은 3차원 히스토그램을 계산하도록 빈칸을 채우시오.&lt;/h3&gt;
&lt;pre id=&quot;code_1751352909438&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import numpy as np, cv2

### calc_histo 정의

def calc_histo(image, channels, bsize, ranges):
    shape = bsize if len(channels) &amp;gt; 1 else(bsize[0], 1)
    hist = np.zeros(shape, dtype=np.float32)
    gap = np.divide(ranges[1::2], bsize[0])
    for row in image:
        for val in row:
            idx = np.divide(val[channels], gap).astype('uint')
            hist[tuple(idx)] += 1
    return hist

### cv2.calcHist()와 calc_histo()가 같은 결과를 내는지 테스트

image = cv2.imread(&quot;images/hue_hist.jpg&quot;, cv2.IMREAD_COLOR)   # 영상읽기
if image is None: raise Exception(&quot;영상 파일 읽기 오류&quot;)

bsize, ranges = [64, 64, 64], [0,256,0,256,0,256]     

my_hist = calc_histo(image, [0,1,2], bsize, ranges).astype('uint8')
cv_hist = cv2.calcHist([image], [0,1,2], None, bsize, ranges).astype('uint8')

diff = (my_hist - cv_hist).all()
print(&quot;cv2.calcHist()와 calc_histo()의 모든 원소의 차이는 %d으로&quot; %diff, &quot;같은&quot; if diff == 0 else &quot;다른&quot;, &quot;결과를 가짐&quot;)

### 시각화로 비교
import matplotlib.pyplot as plt
fig, ax = plt.subplots(2,figsize=(15, 8))

my_b_hist = np.sum(my_hist, axis=(1, 2))  # B만 남기기
my_g_hist = np.sum(my_hist, axis=(0, 2))  # G만 남기기
my_r_hist = np.sum(my_hist, axis=(0, 1))  # R만 남기기

index = np.arange(64)
bar_width = 0.25

ax[0].bar(index, my_b_hist, bar_width,alpha=0.4, color='blue', label='B')
ax[0].bar(index + bar_width, my_g_hist, bar_width, alpha=0.4, color='green', label='G')
ax[0].bar(index + 2 * bar_width, my_r_hist, bar_width, alpha=0.4, color='red', label='R')
ax[0].legend(title='calc_histo()')

cv_b_hist = np.sum(cv_hist, axis=(1, 2))  # B만 남기기
cv_g_hist = np.sum(cv_hist, axis=(0, 2))  # G만 남기기
cv_r_hist = np.sum(cv_hist, axis=(0, 1))  # R만 남기기

ax[1].bar(index, cv_b_hist, bar_width,alpha=0.4, color='blue', label='B')
ax[1].bar(index + bar_width, cv_g_hist, bar_width, alpha=0.4, color='green', label='G')
ax[1].bar(index + 2 * bar_width, cv_r_hist, bar_width, alpha=0.4, color='red', label='R')
ax[1].legend(title='cv2.calcHist()')

plt.show()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;509&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjsO6j/btsOXSmcLgD/TOM1YKLBUi2A1dzlmh5zI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjsO6j/btsOXSmcLgD/TOM1YKLBUi2A1dzlmh5zI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjsO6j/btsOXSmcLgD/TOM1YKLBUi2A1dzlmh5zI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjsO6j%2FbtsOXSmcLgD%2FTOM1YKLBUi2A1dzlmh5zI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;509&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;509&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;13. 컬러 영상을 입력받아서 YCbCr 컬러 공간으로 변환하고 다시 환원하는 프로그램을 작성하시오. 단, cv2.cvtColor() 함수를 사용하지 않고, YCbCr 변환 수식에 따라서 직접 구현하시오.&lt;/h3&gt;
&lt;pre id=&quot;code_1751352967757&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import numpy as np, cv2

BGR_img = cv2.imread(&quot;images/color_space.jpg&quot;, cv2.IMREAD_COLOR) # 컬러 영상 읽기
if BGR_img is None: raise Exception(&quot;영상 파일 읽기 오류&quot;)

def BGR2YCrCb(img):
    B,G,R = cv2.split(img.astype(np.float32))
    Y = 0.299 * R + 0.587 * G + 0.114 * B
    Cr = (R - Y) * 0.713 + 128
    Cb = (B - Y) * 0.564 + 128
    YCrCb = cv2.merge([Y, Cr, Cb])
    YCrCb = np.clip(YCrCb, 0, 255).astype(&quot;uint8&quot;)

    return YCrCb

def YCrCb2BGR(img):
    Y, Cr, Cb = cv2.split(img.astype(np.float32))
    R = Y + 1.403*(Cr-128)
    G = Y - 0.714*(Cr-128) - 0.344 * (Cb-128)
    B = Y + 1.773*(Cb-128)
    BGR = cv2.merge([B,G,R])
    BGR = np.clip(BGR, 0, 255).astype(&quot;uint8&quot;)

    return BGR

YCC_img = BGR2YCrCb(BGR_img) # YCbCr 컬러 공간 변환  
Y, Cr, Cb = cv2.split(YCC_img)
YCC_merged = np.hstack((Y, Cr, Cb))
restore_BGR_img = YCrCb2BGR(YCC_img)

cv2.imshow(&quot;Original BGR_img&quot;, BGR_img)
cv2.imshow(&quot;restored BGR_img&quot;, restore_BGR_img)
cv2.imshow(&quot;YCC_img: Y, Cr, Cb &quot;, YCC_merged)
cv2.waitKey(0)
cv2.destroyAllWindows()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;623&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVoFlp/btsOZwWVduD/BdSZLBNhbfasrMF9peid90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVoFlp/btsOZwWVduD/BdSZLBNhbfasrMF9peid90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVoFlp/btsOZwWVduD/BdSZLBNhbfasrMF9peid90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVoFlp%2FbtsOZwWVduD%2FBdSZLBNhbfasrMF9peid90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;623&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;623&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;14. 컬러 영상 파일을 읽어 들여서 HSV 컬러 공간으로 변환하고, Hue와 Saturation 채널로 2차원 히스토그램을 그림과 같이 만드시오. 즉, 2차원 히스토그램의 Hue(세로)와 Saturation(가로)을 2개 축으로 구성하고, 빈도값을 밝기로 표현해서 오른쪽 그림과 같이 2차원 그래프로 그리시오.&lt;/h3&gt;
&lt;pre id=&quot;code_1751353021078&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import numpy as np, cv2

def draw_histo2(hist):
    if hist.ndim != 2: print('2차원 히스토그램 아님')
    h, w = hist.shape[:2]
    graph = [[(i, j, hist[i,j]) for j in range(w)] for i in range(h)]
    ratios = (180/h, 256/w, 256 )
    graph= np.multiply(graph, ratios).astype('uint8')
    bgr = cv2.cvtColor(graph, cv2.COLOR_HSV2BGR)
    bgr = cv2.resize(bgr, None, fx=10, fy=10, interpolation=cv2.INTER_AREA)

    return bgr

image = cv2.imread(&quot;images/Pink_flower.jpg&quot;, cv2.IMREAD_COLOR)   # 영상읽기
if image is None: raise Exception(&quot;영상 파일 읽기 오류&quot;)

hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
ch, bsize, ranges = [0, 1], [30, 48], [0, 180, 0, 256]  # 히스토그램 간격수, 값 범위
hist = cv2.calcHist([hsv], ch, None, bsize, ranges)  # OpenCV 함수
cv2.normalize(hist, hist, 0, 1, cv2.NORM_MINMAX)
dst = draw_histo2(hist)

cv2.imshow(&quot;dst&quot;, dst)
cv2.waitKey(0)
cv2.destroyAllWindows()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;753&quot; data-origin-height=&quot;520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HyoYo/btsOZehWGYe/KmaIarkqDPmoWG4A063EX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HyoYo/btsOZehWGYe/KmaIarkqDPmoWG4A063EX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HyoYo/btsOZehWGYe/KmaIarkqDPmoWG4A063EX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHyoYo%2FbtsOZehWGYe%2FKmaIarkqDPmoWG4A063EX0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;753&quot; height=&quot;520&quot; data-origin-width=&quot;753&quot; data-origin-height=&quot;520&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Programming/Python</category>
      <category>opencv-python</category>
      <category>opencv-python으로 배우는 영상처리 및 응용</category>
      <category>연습문제 6장</category>
      <author>FoO__511</author>
      <guid isPermaLink="true">https://foo-511.tistory.com/21</guid>
      <comments>https://foo-511.tistory.com/21#entry21comment</comments>
      <pubDate>Tue, 1 Jul 2025 12:03:17 +0900</pubDate>
    </item>
    <item>
      <title>[OpenCV] OpenCV-Python으로 배우는 영상 처리 및 응용 - 5장 연습문제 풀이</title>
      <link>https://foo-511.tistory.com/20</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. OpenCV의 채널 처리 함수에 대해 아는 대로 기술하시오.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;컬러 영상은 파란색, 녹색, 빨간색의 각기 독립적인 2창원 정보를 합쳐 놓은 배열로 정의가 가능하며, 이를 표현하기 위해 채널이라는 개념이 도입됐다. 일련의 3개 원소로 하나의 컬러 화소가 구성되며, numpy에서는 화소 단위로 순회한다. 이 컬러 배열을 분리하면 각 채널을 단일채널 행렬로 구성할 수 있으며, 세부적인 영상처리에 이용될 수 있다. (교재 162p)&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2의 채널 처리 함수로는 cv2.merge()와 cv2.split()이 있으며, 각 함수는 아래와 같다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot; colspan=&quot;3&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.merge(mv[,dst]) &amp;rarr; dst&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;▪ &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;설명: 여러 개의 단일채널 배열을 다채널 배열로 합성한다.&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td rowspan=&quot;2&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;인수 설명&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;mv&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;합성될 입력 혹은 배열 벡터, 합성될 단일채널 배열들의 크기와 깊이(depth)가 동일해야 함&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;dst&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;입력 배열과 같은 크기와 같은 깊이의 출력 배열&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot; colspan=&quot;3&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.split(m[, mv]) &amp;rarr; mv&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;▪ 설명: 다채널 배열을 여러 개의 단일채널 배열로 분리한다.&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td rowspan=&quot;2&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;인수 설명&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;m&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;입력되는 다채널 배열&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;mv&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;분리되어 반환되는 단일채널 배열들의 벡터&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(교재 165p)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. OpenCV의 사칙 연산을 수행하는 함수와 연산의 수행 방법에 대해 기술하시오.&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;두 배열의 원소간(per-element) 사칙 연산을 수행하도록 cv2.add(), cv2.substract(), cv2.multiply(), cv2.divide() 함수를 제공한다. 이 함수들은 두 행렬의 원소간에 연산을 수행한다. 이때, 마스크 행렬을 이용해서 특정 영역만 연산을 수행할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(교재 208p)&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot; colspan=&quot;3&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.add(src, src2[,dst[, mask[, dtype]]]) &amp;rarr; dst&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;▪ &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;설명: 두 개의 배열 혹은 배열과 스칼라의 각 원소 간 합을 계산한다. 입력 인수 src1, src2 중 하나는 스칼라값일 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;▪ 수식:&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;dst&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;=saturate&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;src1&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;+src2&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; if mask&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;ne;0&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;dst&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;=saturate&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;src1+src2&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; if mask&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;ne;0&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;dst&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;=saturate&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;src1&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;+src2&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; if mask&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;ne;0&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td rowspan=&quot;5&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;인수 설명&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;src1&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;첫 번째 입력 배열 혹은 스칼라&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;src2&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;두 번째 입력 배열 혹은 스칼라&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;dst&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;계산된 결과의 출력 배열&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;mask&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;연산 마스크: 0이 아닌 마스크 원소의 위치만 연산 수행(8비트 단일채널)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;dtype&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;출력 배열의 깊이&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot; colspan=&quot;3&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.substract(src1, src2[, dst[, mask[, dtype]]]) &amp;rarr; dst&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;▪ &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;설명: 두 개의 배열 혹은 배열과 스칼라의 각 원소 간 차분을 계산한다. add()함수의 인수와 동일하다.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;▪ 수식:&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;dst&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;=saturate&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;src1&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;-src2&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; if mask&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;ne;0&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;dst&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;=saturate&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;src1-src2&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; if mask&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;ne;0&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;dst&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;=saturate&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;src1&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;-src2&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; if mask&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;ne;0&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot; colspan=&quot;3&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.multiply(src1, src2[, dst[, scale[, dtype]]]) &amp;rarr; dst&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;▪ &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;설명: 두 배열의 각 원소 간 곱을 계산한다.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;▪ 수식:&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;dst&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;=saturate&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;scale&amp;sdot;src1&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;sdot;src2&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;인수 설명&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;scale&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;두 배열의 운소 간 곱할 때 추가로 곱해주는 배율&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot; colspan=&quot;3&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.divide(src1, src2[, dst[, scale[, dtype]]]) &amp;rarr; dst&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;▪ &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;설명: 두 배열의 각 원소 간 나눗샘을 수행한다.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;▪ 수식:&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;dst&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;=saturate&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;scale&amp;sdot;src1&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;/src2(i)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot; colspan=&quot;3&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.divide(scale, src2[, dst[, dtype]]) &amp;rarr; dst&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;▪ &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;설명: 스칼라값과 행렬원소간 나눗셈을 수행한다.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;▪ 수식:&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;dst&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;=scale/src2(i)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot; colspan=&quot;3&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.addWeighted(src1, alpha, src2, beta, gamma[, dst[, dtype]]) &amp;rarr; dst&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;▪ &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;설명: 두 배열의 각 원소에 가중치를 곱한 수에 각 원소 간 합 즉, 가중된(weighted) 합을 계산한다.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;▪ 수식:&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;dst&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;=saturate&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;src1&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;sdot;alpha+src2&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;sdot;beta+gamma&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td rowspan=&quot;3&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;인수 설명&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;alpha&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;첫 번째 배열의 모든 원소에 대한 가중치&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;beta&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;두 번째 배열의 모든 원소에 대한 가중치&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;gamma&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;두 배열의 원소 간 합에 추가로 더해주는 스칼라&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(교재 170-171p)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. 행렬(ndarray)을 초기화하는 방법들에 대해서 기술하고, 각 방법으로 선언하시오.&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;numpy에서 다양한 행렬 초기화 메소드를 제공한다. np.zeros()는 0으로 채워진 행렬을 초기화하며, np.ones()는 1로 채워진 행렬, np.full()은 지정된 값으로 채워진 행렬을 반환한다.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;np.zeros()&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;np.zeros((2,4), np.uint8)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;# 0으로 채워진 2행 4열 uint8 자료형의 행렬 생성&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;np.ones()&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;np.ones((2,4), np.uint8)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;# 1으로 채워진 2행 4열 uint8 자료형의 행렬 생성&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;np.full()&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;np.full((2,4), 30, np.uint8)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;# 30으로 채워진 2행 4열 uint8 자료형의 행렬 생성&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4. 사칙 연산이나 논리 비트 연산에서 마스킹(masking)을 사용할 수 있다. 마스크 행렬에 대한 의미와 사용법에 대해서 설명하시오.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;사칙 연산 함수, 논리 비트 연산 함수에서 입력 인수로 mask를 제공한다. Mask는 8비트 단일채널 배열을 가지며, 마스크 배열의 원소가 0이 아닌 좌표만 계산을 수행한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예를 들어 마스크 배열이 [[0, 1], [1, 1]]의 구조를 가진다면 값이 0인 (0, 0) 좌표는 해당 함수가 수행하는 연산을 수행하지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;[사용 예시]&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;소스코드&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;import numpy as np, cv2&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;src1 = np.arange(4, dtype=np.uint8).reshape(2,2)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;src2 = np.arange(4, 8, dtype=np.uint8).reshape(2,2)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;mask = np.array([0,1,1,1], dtype=np.uint8).reshape(2,2)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;add_without_mask = cv2.add(src1, src2)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;add_with_mask = cv2.add(src1, src2, mask=mask)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;print('add_without_mask =\n', add_without_mask)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;print('add_with_mask =\n', add_with_mask)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;163&quot; data-origin-height=&quot;114&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQMApS/btsOWj4itoz/RLYanJfKnXJV6g5NkHQMpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQMApS/btsOWj4itoz/RLYanJfKnXJV6g5NkHQMpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQMApS/btsOWj4itoz/RLYanJfKnXJV6g5NkHQMpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQMApS%2FbtsOWj4itoz%2FRLYanJfKnXJV6g5NkHQMpk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;163&quot; height=&quot;114&quot; data-origin-width=&quot;163&quot; data-origin-height=&quot;114&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;5. cv2.reduce() 함수에 대해서 설명하고, 특히 축소 시에 사용하는 연산 옵션에 대해서 상세히 설명하시오.&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot; colspan=&quot;3&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.reduce(src, dim, rtype[, dst[, dtype]]) &amp;rarr; dst&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;▪ &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;설명: 행렬을 열방향/행방향으로 옵션 상수(rtype)에 따라 축소한다.&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td rowspan=&quot;5&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;인수 설명&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;src&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2차원 입력 배열(np.float32, np.float64에서만 수행 가능)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;dst&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;출력 벡터, 감소방향과 타입은 dim, dtype 인수에 따라 정해짐&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;dim&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;행렬이 축소소딜 때 차원 감소 첨자&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc; color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;0 : 열 방향으로 연산하여 1행으로 축소&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc; color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1 : 행 방향으로 연산하여 1열로 감소&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;rtype&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;축소 연산 종류&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;dtype&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;감소된 벡터의 자료형&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;6. 다음 예시 코드는 컴파일 에러가 발생한다. 에러가 발생하는 부분을 수정하고 실행 결과를 적으시오.&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 292px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;background-color: #e7e6e6; height: 21px;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;소스코드&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 215px;&quot;&gt;
&lt;td style=&quot;height: 215px;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;import numpy as np, cv2&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;# m1과 m2를 list에서 ndarray로 수정&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;m1 = np.array([1,2,3,1,2,3])&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;m2 = np.array([3,3,4,2,2,3])&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;m3 = m1 + m2&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;m4 = m1 - m2&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;print(&quot;[m1] = %s&quot; % m1)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;print(&quot;[m2] = %s&quot; % m2)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;print(&quot;[m3] = %s&quot; % m3)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;print(&quot;[m3] = %s&quot; % m3)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;160&quot; data-origin-height=&quot;71&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwxDIy/btsOYgSXO1o/MPeMay505tl16E5TfIZkK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwxDIy/btsOYgSXO1o/MPeMay505tl16E5TfIZkK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwxDIy/btsOYgSXO1o/MPeMay505tl16E5TfIZkK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwxDIy%2FbtsOYgSXO1o%2FMPeMay505tl16E5TfIZkK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;160&quot; height=&quot;71&quot; data-origin-width=&quot;160&quot; data-origin-height=&quot;71&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;소스코드&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;import numpy as np, cv2&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;data = [1,2,3,4,5,6,7,8,9,10,11,12]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;# reshape(2,3,2)를 하면 2채널이 되어 rgb 3개의 채널로 분리가 불가함. 따라서 reshape(2,2,3)으로 수정&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;m1 = np.array(data).reshape(2,2,3)&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;r,g,b = cv2.split(m1)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;print(&quot;[m1] = %s&quot; % m1)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;print(&quot;[r, g, b] = %s, %s, %s&quot; % (r,g,b))&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;177&quot; data-origin-height=&quot;156&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2KRkW/btsOVeim1vM/6SrLq0a6RYLG3Dkt4vzm40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2KRkW/btsOVeim1vM/6SrLq0a6RYLG3Dkt4vzm40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2KRkW/btsOVeim1vM/6SrLq0a6RYLG3Dkt4vzm40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2KRkW%2FbtsOVeim1vM%2F6SrLq0a6RYLG3Dkt4vzm40%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;177&quot; height=&quot;156&quot; data-origin-width=&quot;177&quot; data-origin-height=&quot;156&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;7. 다음의 컬러 영상파일(logo.jpg)을 입력 받아서 RGB의 3개 채널을 분리하고, 각 채널을 컬러 영상을 윈도우에 표시해 보자. 즉 Red 채널은 빨간색으로, green 채널은 초록색으로, Blue 채널은 파란색으로 표현되도록 다음의 프로그램을 완성하시오.&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;소스코드&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;import numpy as np, cv2&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;logo = cv2.imread('images/logo.jpg', cv2.IMREAD_COLOR)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;if logo is None: raise Exception(&quot;영상파일 읽기 오류&quot;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;blue, green, red = cv2.split(logo)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;zeros = np.zeros(blue.shape, dtype=np.uint8)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;blue_img = cv2.merge([blue, zeros, zeros])&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;green_img = cv2.merge([zeros, green, zeros])&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;red_img = cv2.merge([zeros, zeros, red])&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;# 윈도우 자동 정렬&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;titles = ['logo']+[_+&quot;_img&quot; for _ in [&quot;blue&quot;, &quot;green&quot;, &quot;red&quot;]]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;x, y = 100, 100&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;for title in titles:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cv2.namedWindow(title, cv2.WINDOW_AUTOSIZE)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cv2.moveWindow(title, x, y)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;x += blue.shape[1] + 10&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cv2.imshow(title, eval(title))&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.waitKey(0)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.destroyAllWindows()&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1051&quot; data-origin-height=&quot;356&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6XqWO/btsOVDiikMg/P2ZvYCTyoJS7zSxKFAhe50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6XqWO/btsOVDiikMg/P2ZvYCTyoJS7zSxKFAhe50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6XqWO/btsOVDiikMg/P2ZvYCTyoJS7zSxKFAhe50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6XqWO%2FbtsOVDiikMg%2FP2ZvYCTyoJS7zSxKFAhe50%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;204&quot; data-origin-width=&quot;1051&quot; data-origin-height=&quot;356&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;8. 다음 영상에서 특정 영역의 타원반을 복사하여 새 창에 표시하는 프로그램을 완성하시오.&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;소스코드&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;import numpy as np, cv2&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;image = cv2.imread(&quot;images/color.jpg&quot;, cv2.IMREAD_COLOR)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;if image is None: raise Exception(&quot;영상파일 읽기 오류&quot;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;mask = np.zeros(image.shape[:2], np.uint8)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;center = (190, 170)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.ellipse(mask, center, (30,60), 0,0,360,255, -1)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;dst = cv2.bitwise_and(image, image, mask=mask)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;# 윈도우 정렬&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;x = y = 100&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;for title in ['image', 'dst']:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cv2.namedWindow(title, 1)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cv2.moveWindow(title, x, y)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;x += image.shape[1] + 10&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cv2.imshow(title, eval(title))&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.waitKey(0)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.destroyAllWindows()&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;981&quot; data-origin-height=&quot;404&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3lBSL/btsOVDJoB3B/gHokjHvaNa3E2cRqgXcwY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3lBSL/btsOVDJoB3B/gHokjHvaNa3E2cRqgXcwY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3lBSL/btsOVDJoB3B/gHokjHvaNa3E2cRqgXcwY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3lBSL%2FbtsOVDJoB3B%2FgHokjHvaNa3E2cRqgXcwY1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;248&quot; data-origin-width=&quot;981&quot; data-origin-height=&quot;404&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;9. 3행, 6열의 행렬을 생성하고, 행렬의 원소를 초기화한 후에 cv2.reduce() 함수를 이용해서 가로 방향과 세로 방향으로 감축하여 평균을 구한 결과를 출력하시오.&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;소스코드&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;import numpy as np, cv2&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;arr = np.random.rand(3, 6) * 1000//10&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;col_avg = cv2.reduce(arr, dim=0, rtype=cv2.REDUCE_AVG) # 행방향 축소 -&amp;gt; 열 평균&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;row_avg = cv2.reduce(arr, dim=1, rtype=cv2.REDUCE_AVG) # 열방향 축소 -&amp;gt; 행 평균&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;print('arr = \n', arr)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;print('열 평균 = ', col_avg.flatten().round(2))&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;print('행 평균 = ', row_avg.flatten().round(2))&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;367&quot; data-origin-height=&quot;106&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVSelD/btsOWlA1rVy/cYsOSSJEoqbPyuzVckMpW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVSelD/btsOWlA1rVy/cYsOSSJEoqbPyuzVckMpW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVSelD/btsOWlA1rVy/cYsOSSJEoqbPyuzVckMpW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVSelD%2FbtsOWlA1rVy%2FcYsOSSJEoqbPyuzVckMpW0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;367&quot; height=&quot;106&quot; data-origin-width=&quot;367&quot; data-origin-height=&quot;106&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;10. PC 카메라로 영상을 읽어서 특정 부분(관심 영역)의 합과 평균을 구하는 프로그램을 작성하시오.&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;소스코드&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;import numpy as np, cv2&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;capture = cv2.VideoCapture(1)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;if capture.isOpened() == False:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;raise Exception(&quot;카메라 연결 안됨&quot;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;def print_info(frame, slices=(slice(200, 400), slice(100, 300))):&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;target = frame[slices]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cv2_mean = cv2.mean(target)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;target_sum = np.zeros(3)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for y in range(target.shape[0]):&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for x in range(target.shape[1]):&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;target_sum += target[y][x]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;user_mean = target_sum / (target.shape[0] * target.shape[1])&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&quot;cv2로 구한 평균 = &quot;, cv2_mean)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&quot;원소 순회로 구한 평균 = &quot;, tuple(user_mean))&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&quot;관심영역 합 = &quot;, target_sum)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;while True:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ret, frame = capture.read()&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if not ret: break&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if cv2.waitKey(30) &amp;gt;= 0: break&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print('[밝은 부분]')&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print_info(frame, (slice(200, 400), slice(100, 300)))&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print('[어두운 부분]')&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print_info(frame, (slice(-200, -100), slice(-200, -100)))&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;exposure = capture.get(cv2.CAP_PROP_EXPOSURE)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;title = &quot;View Frame from Camera&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cv2.imshow(title, frame)&amp;nbsp; # 윈도우에 영상 띄우기&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;capture.release()&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.destroyAllWindows()&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;866&quot; data-origin-height=&quot;270&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vA2kQ/btsOVtTO7pm/8hWWM8thrfGvifCJPKSpt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vA2kQ/btsOVtTO7pm/8hWWM8thrfGvifCJPKSpt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vA2kQ/btsOVtTO7pm/8hWWM8thrfGvifCJPKSpt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvA2kQ%2FbtsOVtTO7pm%2F8hWWM8thrfGvifCJPKSpt1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;188&quot; data-origin-width=&quot;866&quot; data-origin-height=&quot;270&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;11. PC 카메라로 영상을 받아들여서 다음과 같이 윈도우의 특정 영역에서 재생하시오.&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;소스코드&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;import numpy as np, cv2&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;capture = cv2.VideoCapture(1)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;if capture.isOpened() == False:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;raise Exception(&quot;카메라 연결 안됨&quot;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;bg = np.zeros((300, 400, 3), dtype=np.uint8)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;mask = np.zeros((300, 400), dtype=np.uint8)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.rectangle(mask, (30, 30), (30+320, 30+240), 255, -1)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;while True:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ret, frame = capture.read()&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if not ret: break&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if cv2.waitKey(30) &amp;gt;= 0: break&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;target = cv2.resize(frame, (400, 300))&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dst = cv2.bitwise_and(target, target, mask=mask)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cv2.rectangle(dst, (30, 30), (30+320, 30+240), (0,0,255), 2)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;exposure = capture.get(cv2.CAP_PROP_EXPOSURE)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;title = &quot;View Frame from Camera&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cv2.imshow(title, dst)&amp;nbsp; # 윈도우에 영상 띄우기&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;capture.release()&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.destroyAllWindows()&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;12. 영상파일을 읽어서 메인 윈도우에 다음과 같이 출력하시오.&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;소스코드&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;image = cv2.imread('images/abs_test1.jpg', cv2.IMREAD_GRAYSCALE)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;if image is None: raise Exception(&quot;영상파일 읽기 오류&quot;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;mask1 = np.zeros(image.shape[:2], image.dtype)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.rectangle(mask1, (50, 50, 100, 100), 255, -1)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.add(image, 50, image, mask1)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;target = image[200:300, 200:300]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;noimage = np.zeros(target.shape[:2], target.dtype)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;avg = cv2.mean(image)[0]/2.0&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;target = cv2.addWeighted(target, 2.0, noimage, 0, -avg)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;image[200:300, 200:300] = target&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.imshow(&quot;12&quot;, image)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.waitKey(0)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.destroyAllWindows()&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;501&quot; data-origin-height=&quot;408&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OokB7/btsOXVVNqVx/LMAsVvz4OPK0jL7yGIL04K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OokB7/btsOXVVNqVx/LMAsVvz4OPK0jL7yGIL04K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OokB7/btsOXVVNqVx/LMAsVvz4OPK0jL7yGIL04K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOokB7%2FbtsOXVVNqVx%2FLMAsVvz4OPK0jL7yGIL04K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;501&quot; height=&quot;408&quot; data-origin-width=&quot;501&quot; data-origin-height=&quot;408&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;13. cv2.sortIdx() 함수를 활용해서 다음의 조건에 부합하도록 벡터의 원소를 정렬하시오.&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;소스코드&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;import numpy as np, cv2&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;def print_rects(rects):&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&quot;-&quot; * 46)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&quot;사각형 원소\t\t랜덤 사각형 정보\t\t &amp;nbsp; 넓이&quot;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&quot;-&quot; * 46)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for i, (x,y, w,h, a) in enumerate(rects):&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&quot;rects[%i] = [(%3d,%3d) from (%3d,%3d)] %5d&quot; %(i, w, h, x, y, a))&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print()&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;rands = np.zeros((10, 5), np.uint16)&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;starts = cv2.randn(rands[:, :2 ], 100, 50) &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ends = cv2.randn(rands[:, 2:-1], 300, 50) &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;sizes = cv2.absdiff(starts, ends)&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;areas = sizes[:, 0] * sizes[:, 1]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;rects = rands.copy()&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;rects[:, 2:-1] = sizes&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;rects[:,-1] = areas&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;idx = cv2.sortIdx(areas, cv2.SORT_EVERY_COLUMN + cv2.SORT_ASCENDING).flatten()&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;print_rects(rects)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;print_rects(rects[idx.astype('int')])&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;535&quot; data-origin-height=&quot;456&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xz9Mj/btsOXjbNuGL/DVvxJ4GeDCuUwuX2AsgJM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xz9Mj/btsOXjbNuGL/DVvxJ4GeDCuUwuX2AsgJM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xz9Mj/btsOXjbNuGL/DVvxJ4GeDCuUwuX2AsgJM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxz9Mj%2FbtsOXjbNuGL%2FDVvxJ4GeDCuUwuX2AsgJM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;535&quot; height=&quot;456&quot; data-origin-width=&quot;535&quot; data-origin-height=&quot;456&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;14. 다음의 연립방정식을 가우시안 소거법의 역함수를 계산해서 해를 구하는 프로그램을 작성하시오.&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;소스코드&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;import numpy as np, cv2&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;data = [ 3, 6, 3, -5, 6, 1, 2,-3, 5]&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;m1 = np.array(data, np.float32).reshape(3,3)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;m2 = np.array([2, 10, 28], np.float32)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ret, inv = cv2.invert(m1, cv2.DECOMP_LU)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;if ret:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ret, dst = cv2.solve(m1, m2, cv2.DECOMP_LU)&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 연립방정식 풀이&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&quot;[inv] = \n%s\n&quot; % inv)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&quot;[dst] =&quot;, dst.flatten()) &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 행렬을 벡터로 변환&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;else:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&quot;역행렬이 존재하지 않습니다.&quot;)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;324&quot; data-origin-height=&quot;113&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpbqh3/btsOWf1QFVK/tTZ4uvaXkD2Y1EDQtofdN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpbqh3/btsOWf1QFVK/tTZ4uvaXkD2Y1EDQtofdN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpbqh3/btsOWf1QFVK/tTZ4uvaXkD2Y1EDQtofdN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbpbqh3%2FbtsOWf1QFVK%2FtTZ4uvaXkD2Y1EDQtofdN0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;324&quot; height=&quot;113&quot; data-origin-width=&quot;324&quot; data-origin-height=&quot;113&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;15. 5.6절에서 예제_5.6.2의 사각형 회전하기 예제를 확장하여 그 사각형의 중심점을 기준으로 45도 회전시키는 프로그램을 완성하시오.&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;소스코드&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;import numpy as np, cv2&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;pts1 = np.array([(200,50,1), (400, 50, 1), (400, 250, 1), (200, 250, 1)], np.float32)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;theta = 45 * np.pi / 180&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;m = np.array([[np.cos(theta), -np.sin(theta), 0],&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[np.sin(theta), np.cos(theta), 0],&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[0,0,1]], np.float32)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;delta = (pts1[2] - pts1[0])// 2&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;center = pts1[0] + delta&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;print('center=',center)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;t1 = np.eye(3, dtype=np.float32)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;t2 = np.eye(3, dtype=np.float32)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;t1[:2, 2] = -center[:2]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;t2[:2, 2] = center[:2]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;m2 = cv2.gemm(t2, m, 1, None, 0)&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;m2 = cv2.gemm(m2, t1, 1, None, 0)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;pts2 = cv2.gemm(pts1, m2, 1,None, 1, flags=cv2.GEMM_2_T)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;for i, (pt1, pt2) in enumerate(zip(pts1, pts2)):&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&quot;pts1[%d] = %s, pts2[%d] = %s&quot;%(i, pt1, i, pt2))&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;image = np.full((400, 500, 3), 255, np.uint8)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.polylines(image, [np.int32(pts1[:,:2])], True, (0, 255, 0), 2)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.polylines(image, [np.int32(pts2[:,:2])], True, (255, 0, 0), 3)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.imshow(&quot;image&quot;, image)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.waitKey(0)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.destroyAllWindows()&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;515&quot; data-origin-height=&quot;446&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NxOyv/btsOVCqbRiL/qkV7aICwW57PFMKdvbX72k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NxOyv/btsOVCqbRiL/qkV7aICwW57PFMKdvbX72k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NxOyv/btsOVCqbRiL/qkV7aICwW57PFMKdvbX72k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNxOyv%2FbtsOVCqbRiL%2FqkV7aICwW57PFMKdvbX72k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;515&quot; height=&quot;446&quot; data-origin-width=&quot;515&quot; data-origin-height=&quot;446&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docs에서 쓴거 그대로 옮겼더니 수식이 다 깨졌다.&lt;/p&gt;</description>
      <category>Programming/Python</category>
      <category>opencv-python</category>
      <category>opencv-python으로 배우는 영상처리 및 응용</category>
      <category>연습문제 풀이</category>
      <author>FoO__511</author>
      <guid isPermaLink="true">https://foo-511.tistory.com/20</guid>
      <comments>https://foo-511.tistory.com/20#entry20comment</comments>
      <pubDate>Mon, 30 Jun 2025 09:14:25 +0900</pubDate>
    </item>
    <item>
      <title>[OpenCV] OpenCV-Python으로 배우는 영상 처리 및 응용 - 4장 연습문제 풀이</title>
      <link>https://foo-511.tistory.com/19</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 콜백&amp;nbsp;함수란 무엇인가?&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;콜백 함수는 일반적으로 이벤트를 처리하기 위한 함수로, 개발자가 시스템 함수를 직접 호출하는 방식이 아니라, 어떤 이벤트가 발생하거나 특정 시점에 도달했을 때 시스템이 개발자가 등록한 함수를 호출하는 방식이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(교재 96p)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. 윈도우를 지정하는 cv2.namedWindow() 함수의 두 번째 인수(flags)에 대한 옵션은 여러가지가 있다. 그 중에서 cv2.WINDOW_NORMAL와 cv2.WINDOW_AUTOSIZE간의 차이를 설명하시오.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;두 번째 인수 flags는 인도우의 크기 조정과 관련된 파라미터이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.WINDOW_NORMAL은 0의 값을 가지며, 윈도우 크기 조정이 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.WINDOW_AUTOSIZE는 1의 값을 가지며, 표시된 행렬의 크기에 맞춰 자동 조정된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(교재 92p)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. 타원을 그리는 cv2.ellipses() 함수의 인수를 자세히 설명하시오.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;[함수 설명]&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.ellipse(img, center, axes, angle, startAngle, endAngle, color[, thickness[, lineType[, shift]]]) &amp;rarr; img&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;center를 중심으로 axes 크기의 타원을 그린다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;[인수 설명]&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;인수&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;설명&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;옵션 여부&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;img&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그릴 대상 행렬(영상)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;필&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;center&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;원의 중심 좌표&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;필&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;axes&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;타원의 절반 크기(x축 반지름, y축 반지름)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;필&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;angle&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;타원의 각도(3시 방향이 0도, 시계방향 회전)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;필&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;startAngle&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;호의 시작 각도&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;필&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;endAngle&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;호의 종료 각도&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;필&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;color&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;선의 색상&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;필&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;thickness&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;선의 두께&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;옵&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;lineType&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;선의 형태&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;옵&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;shift&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;좌표에 대한 비트 시프트&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;옵&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(교재 116p)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4. OpenCV이 제공하는, 마우스 이벤트와 트랙바 이벤트를 제어할 콜백 함수를 시스템에 등록하는 함수는 각각 무엇이며, 인수가 어떻게 구성되었는지 자세히 설명하시오.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1) 마우스 이벤트 제어&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.setMouseCallback(windowName, onMouse, param=None) &amp;rarr; None&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;사용자가 정의한 마우스 콜백 함수를 시스템에 등록&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;인수&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;설명&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;옵션 여부&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;windowName&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이벤트 발생을 확인할 윈도우 이름, 문자열&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;필&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;onMouse&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;마우스 이벤트를 처리하는 콜백 함수 이름(콜백 함수)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;필&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;param&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이벤트 처리 함수로 전달할 추가적인 사용자 정의 인수&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;옵(기본 None)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(교재 100p)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2) 트랙바 이벤트 제어&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.createTrackbar(trackbarname, winname, value count, onChange) &amp;rarr; None&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;트랙바를 생성한 후, 지정한 윈도우에 추가하는 함수.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;인수&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;설명&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #e7e6e6;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;옵션 여부&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;trackbarname&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;윈도우에 생성되는 트랙바 이름&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;필&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;winname&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;트랙바의 부모 윈도우 이름(트랙바 이벤트 발생을 체크하는 윈도우)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;필&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;value&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;트랙바 슬라이더의 위치를 반영하는 값(정수)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;필&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;count&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;트랙바 슬라이더의 최댓값, 최솟값은 항상 0&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;필&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;onChange&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;트랙바 슬라이더의 값이 변경될 때 호출되는 콜백 함수&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;필&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(교재 103p)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;5. 다음 예시 코드의 실행 결과를 설명하시오.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1) 첫번째 코드&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;300행, 400열의 행렬을 생성하고, 행렬의 모든 값을 100으로 초기화한다. 윈도우 이름을 &amp;lsquo;Window&amp;rsquo;로 지정하여, 크기 조정이 가능한 윈도우를 생성한다. 윈도우 위치를 좌측 상단에서 오른쪽으로 100px, 아래쪽으로 200px 떨어진 곳으로 바꾼다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;처음 생성한 행렬을 윈도우에 표시하고 키 입력을 기다린다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;키 입력이 발생하면 윈도우를 없앤다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2) 두번째 코드&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;400행, 600열, 3채널 행렬을 만들고, 모든 채널을 흰색(255, 255, 255)로 초기화 한다. 이렇게 초기화된 흰색 바탕에 (50, 100)에서 (200, 300)으로 이어지는 5px 두께의 초록색 선을 그린다. 또 (200, 300)과 (300, 400)에 꼭짓점을 둔 빨간색으로 채워진 직사각형을 그린다. 이를 &amp;ldquo;Line &amp;amp; Rectangle&amp;rdquo;이라는 이름의 윈도우에 표시하고 키 입력을 기다린다. 키 입력이 생기면 윈도우를 없앤다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;6. 300행, 400열의 행렬을 회색 바탕색(100)으로 생성해서 500행, 600열의 윈도우에 표시하시오.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1751241691214&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 4장 6번
import numpy as np, cv2

image = np.zeros((300,400), np.uint8)
image[:] = 100

title = '4-6'
cv2.namedWindow(title, cv2.WINDOW_NORMAL)
cv2.moveWindow(title, 500, 600)
cv2.waitKey(0)
cv2.destroyAllWindows()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;863&quot; data-origin-height=&quot;940&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1kWYs/btsOYvPWlor/3BblZ2YRnJ2pFVLB0Kc8Nk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1kWYs/btsOYvPWlor/3BblZ2YRnJ2pFVLB0Kc8Nk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1kWYs/btsOYvPWlor/3BblZ2YRnJ2pFVLB0Kc8Nk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1kWYs%2FbtsOYvPWlor%2F3BblZ2YRnJ2pFVLB0Kc8Nk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;655&quot; data-origin-width=&quot;863&quot; data-origin-height=&quot;940&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;7. 다음 예시 코드는 컴파일 혹은 런타임 에러가 발생한다. 에러가 발생하는 부분을 수정하고 실행 결과를 적으시오.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1) 첫번째 코드&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1751241705223&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 4장 7번
import numpy as np, cv2

image = np.zeros((300, 400, 3), np.uint8)
image[:] = (255, 255, 255)

pt1, pt2 = (50, 120), (200, 300)

cv2.line(image, pt1, (100, 200), (255, 0, 0))
cv2.line(image, pt2, (110, 210), (100, 100, 100))
cv2.rectangle(image, pt1, pt2, (255, 0, 255))
cv2.rectangle(image, pt1, pt2, (0, 0, 255))

title = &quot;Line &amp;amp; Rectangle&quot;
cv2.namedWindow(title)
cv2.imshow(title, image)
cv2.waitKey(0)
cv2.destroyAllWindows()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;843&quot; data-origin-height=&quot;371&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qr664/btsOW7WVeRO/A0nt5VqgaKjPqBYVxsQxa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qr664/btsOW7WVeRO/A0nt5VqgaKjPqBYVxsQxa0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qr664/btsOW7WVeRO/A0nt5VqgaKjPqBYVxsQxa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fqr664%2FbtsOW7WVeRO%2FA0nt5VqgaKjPqBYVxsQxa0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;265&quot; data-origin-width=&quot;843&quot; data-origin-height=&quot;371&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.line() 함수에 시작점, 끝점, 색상을 필수로 제공해야 하는데 그렇지 않아 오류가 발생한다. 오류가 나지 않도록 끝점과 색상 정보를 입력하면 사진과 같은 결과가 나온다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2) 두번째 코드&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1751241718840&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 4장 7번
import numpy as np, cv2

def onMouse(event, x, y, flags, param):
    global title
    if event == cv2.EVENT_LBUTTONDOWN:
        pt = (x, y)
        cv2.circle(image, pt, 5, 100, 1)
        cv2.imshow(title, image)
    elif event == cv2.EVENT_RBUTTONDOWN:
        pt1 = (x, y)
        pt2 = (x+30, y+30)
        cv2.rectangle(image, pt1, pt2, 100, 2)
        cv2.imshow(title, image)

image = np.ones((300,300), np.uint8) * 255

title = &quot;Draw Event&quot;
cv2.namedWindow(title)
cv2.imshow(title, image)
cv2.setMouseCallback(title, onMouse)
cv2.waitKey(0)
cv2.destroyAllWindows()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;799&quot; data-origin-height=&quot;478&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjh9Ac/btsOWJPaswC/lOjkYdp5oAPakKcJOkw3Lk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjh9Ac/btsOWJPaswC/lOjkYdp5oAPakKcJOkw3Lk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjh9Ac/btsOWJPaswC/lOjkYdp5oAPakKcJOkw3Lk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcjh9Ac%2FbtsOWJPaswC%2FlOjkYdp5oAPakKcJOkw3Lk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;360&quot; data-origin-width=&quot;799&quot; data-origin-height=&quot;478&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cv2.circle() 함수와 cv2.rectangle() 함수에서 사용되는 pt 변수가 정의되지 않아 오류가 발생한다. 이를 고쳐주면 좌클릭 시 윈도우에 원이 그려지고, 우클릭 시 윈도우에 사각형이 그려진다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;8. 200행, 300열의 행렬을 2개 만들어서 다음과 같이 배치하시오&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1751241734047&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 4장 8번
import numpy as np, cv2

mat1 = np.full((200, 300), 100, np.uint8)
mat2 = np.full((200, 300), 100, np.uint8)

title1, title2 = 'title1', 'title2'
cv2.namedWindow(title1, cv2.WINDOW_AUTOSIZE)
cv2.namedWindow(title2, cv2.WINDOW_AUTOSIZE)
cv2.moveWindow(title1, 0,0)
cv2.moveWindow(title2, 300,200)

cv2.imshow(title1, mat1)
cv2.imshow(title2, mat2)
cv2.waitKey(0)
cv2.destroyAllWindows()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;712&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqFyBe/btsOUX84g4K/k1kVQ6kKLTUkRo3x3fK401/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqFyBe/btsOUX84g4K/k1kVQ6kKLTUkRo3x3fK401/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqFyBe/btsOUX84g4K/k1kVQ6kKLTUkRo3x3fK401/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqFyBe%2FbtsOUX84g4K%2Fk1kVQ6kKLTUkRo3x3fK401%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;554&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;712&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;9. 600행, 400열의 윈도우를 만들고, 영상 안의 (100, 100) 좌표에 200*300 크기의 빨간 색 사각형을 그리시오.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1751241746458&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 4장 9번
# 600행, 400열의 윈도우를 만들고, 영상 안의 (100, 100) 좌표에 200*300 크기의 빨간 색 사각형을 그리시오
import numpy as np, cv2

image = np.full((600, 400, 3), 255, np.uint8)

cv2.rectangle(image, (100, 100), (100+200, 100+300), (0, 0, 255), -1)
cv2.imshow('4-9', image)
cv2.waitKey(0)
cv2.destroyAllWindows()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1207&quot; data-origin-height=&quot;647&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tlaYV/btsOVfuMYiK/GoNNSuWzlnFy5N5qWQ0hUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tlaYV/btsOVfuMYiK/GoNNSuWzlnFy5N5qWQ0hUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tlaYV/btsOVfuMYiK/GoNNSuWzlnFy5N5qWQ0hUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtlaYV%2FbtsOVfuMYiK%2FGoNNSuWzlnFy5N5qWQ0hUK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;323&quot; data-origin-width=&quot;1207&quot; data-origin-height=&quot;647&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;10. 다음의 마우스 이벤트 제어 프로그램을 작성하시오.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1751241758257&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 4장 10번
import numpy as np, cv2

def onMouse(event, x, y, flags, param):
    global title
    if event == cv2.EVENT_LBUTTONDOWN:
        pt = (x, y)
        cv2.circle(image, pt, 20, 100, 1)
        cv2.imshow(title, image)
    elif event == cv2.EVENT_RBUTTONDOWN:
        pt1 = (x, y)
        pt2 = (x+30, y+30)
        cv2.rectangle(image, pt1, pt2, 100, 2)
        cv2.imshow(title, image)

image = np.ones((300,300), np.uint8) * 255

title = &quot;Draw Event&quot;
cv2.namedWindow(title)
cv2.imshow(title, image)
cv2.setMouseCallback(title, onMouse)
cv2.waitKey(0)
cv2.destroyAllWindows()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;761&quot; data-origin-height=&quot;438&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VoIv0/btsOYdor9BO/36UFZu4dWKwUBUCKfsMcHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VoIv0/btsOYdor9BO/36UFZu4dWKwUBUCKfsMcHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VoIv0/btsOYdor9BO/36UFZu4dWKwUBUCKfsMcHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVoIv0%2FbtsOYdor9BO%2F36UFZu4dWKwUBUCKfsMcHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;346&quot; data-origin-width=&quot;761&quot; data-origin-height=&quot;438&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;11. 10번 연습문제에서 다음을 추가하여 프로그램을 작성하시오.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1751241770352&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 4장 11번
import numpy as np, cv2

width = 1
radius = 20

def onMouse(event, x, y, flags, param):
    global title, width, radius
    if event == cv2.EVENT_LBUTTONDOWN:
        pt = (x, y)
        cv2.circle(image, pt, radius, 100, width)
        cv2.imshow(title, image)
    elif event == cv2.EVENT_RBUTTONDOWN:
        pt1 = (x, y)
        pt2 = (x+30, y+30)
        cv2.rectangle(image, pt1, pt2, 100, 2)
        cv2.imshow(title, image)

image = np.ones((300,300), np.uint8) * 255

def onWidthChange(value):
    global width
    width = value
def onRadiusChange(value):
    global radius
    radius = value

title = &quot;Draw Event&quot;
cv2.namedWindow(title)
cv2.imshow(title, image)
cv2.setMouseCallback(title, onMouse)
cv2.createTrackbar('width', title, 1, 10, onWidthChange)
cv2.createTrackbar('radius', title, 1, 50, onRadiusChange)
cv2.waitKey(0)
cv2.destroyAllWindows()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;882&quot; data-origin-height=&quot;672&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/by6kTg/btsOVgNTQK0/3kNlHvB9UzSGVcNkOHbRG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/by6kTg/btsOVgNTQK0/3kNlHvB9UzSGVcNkOHbRG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/by6kTg/btsOVgNTQK0/3kNlHvB9UzSGVcNkOHbRG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fby6kTg%2FbtsOVgNTQK0%2F3kNlHvB9UzSGVcNkOHbRG0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;458&quot; data-origin-width=&quot;882&quot; data-origin-height=&quot;672&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;12. 예제_4.2.3인 05.event_trackbar.py에서 화살표 키로 트랙바를 이동하는 코드를 추가하시오.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1751241785854&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 4장 12번

import numpy as np
import cv2

def onChange(value):												# 트랙바 콜백 함수
    global image                        	# 전역 변수 참조

    add_value = value - int(image[0][0])        	# 트랙바 값과 영상 화소값 차분
    print(&quot;추가 화소값:&quot;, add_value)
    if  add_value &amp;gt; 0 : image = image + add_value            		# 행렬과 스칼라 덧셈 수행
    else: image = image - abs(add_value)  # 행렬과 스칼라 덧셈 수행
    cv2.imshow(title, image)

image = np.zeros((300, 500), np.uint8)           	# 영상 생성

title = 'Trackbar Event'
bar_name = &quot;Brightness&quot;   
cv2.imshow(title, image)

cv2.createTrackbar(bar_name, title, image[0][0], 255, onChange)	# 트랙바 콜백 함수 등록

def onLeftArrow():
    global image, bar_name
    if (image[0][0] &amp;gt;= 10): image -= 10
    cv2.setTrackbarPos(bar_name, title, image[0][0])		# 트랙바 위치 변경 
    cv2.imshow(title, image)
        
def onRightArrow():
    global image, bar_name
    if (image[0][0] &amp;lt; 246): image += 10
    cv2.setTrackbarPos(bar_name, title, image[0][0])		# 트랙바 위치 변경 
    cv2.imshow(title, image)
    
switch_case = {2424832: onLeftArrow,   2555904: onRightArrow, }

while True:									# 무한 반복
    key = cv2.waitKeyEx(100)          		# 100ms 동안 키 이벤트 대기
    if key == 27: break                		# ESC 키 누르면 종료
    try:
        func = switch_case[key]
        func()
    except KeyError:
        result = -1

cv2.destroyWindow(title)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1145&quot; data-origin-height=&quot;836&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NMArE/btsOWGZc9yG/5ZycVKKcwxnzS9T7cPw6Gk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NMArE/btsOWGZc9yG/5ZycVKKcwxnzS9T7cPw6Gk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NMArE/btsOWGZc9yG/5ZycVKKcwxnzS9T7cPw6Gk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNMArE%2FbtsOWGZc9yG%2F5ZycVKKcwxnzS9T7cPw6Gk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;439&quot; data-origin-width=&quot;1145&quot; data-origin-height=&quot;836&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;13. 컬러 영상파일을 적재하여 &amp;ldquo;text.jpg&amp;rdquo;와 &amp;ldquo;tes.png&amp;rdquo; 파일로 각각 저장하시오. 이때 영상 파일을 가장 좋은 화질로 압축하시오.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1751241826628&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import cv2

image = cv2.imread(&quot;read_color.jpg&quot;, cv2.IMREAD_COLOR)
if image is None: raise Exception(&quot;영상 파일 읽기 에러&quot;)
    
params_jpg = (cv2.IMWRITE_JPEG_QUALITY, 100)        # JPEG 화질 설정
params_png = [cv2.IMWRITE_PNG_COMPRESSION, 0]       # PNG 압축 레벨 설정

## 행렬을 영상 파일로 저장
cv2.imwrite(&quot;test.jpg&quot;, image, params_jpg) # 지정 화질로 저장
cv2.imwrite(&quot;tes.png&quot;, image, params_png)
print(&quot;저장 완료&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;241&quot; data-origin-height=&quot;264&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mtLhu/btsOWkIQe2p/cw8sUbLcDk2ljmFerkTD61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mtLhu/btsOWkIQe2p/cw8sUbLcDk2ljmFerkTD61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mtLhu/btsOWkIQe2p/cw8sUbLcDk2ljmFerkTD61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmtLhu%2FbtsOWkIQe2p%2Fcw8sUbLcDk2ljmFerkTD61%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;174&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;241&quot; data-origin-height=&quot;264&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;16. PC 카메라를 통해서 받아온 프레임에 다음의 영상처리를 수행하고, 결과 영상을 윈도우에 표시하는 프로그램을 작성하시오.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1126&quot; data-origin-height=&quot;1002&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1bP3x/btsOXhSnVzD/rckGtDYFkChQxzRxKPwZR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1bP3x/btsOXhSnVzD/rckGtDYFkChQxzRxKPwZR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1bP3x/btsOXhSnVzD/rckGtDYFkChQxzRxKPwZR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1bP3x%2FbtsOXhSnVzD%2FrckGtDYFkChQxzRxKPwZR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;535&quot; data-origin-width=&quot;1126&quot; data-origin-height=&quot;1002&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;17. PC 카메라를 통해서 받아온 프레임을 좌우로 뒤집어서 &amp;ldquo;flip_test.avi&amp;rdquo;이름의 동영상 파일로 저장하는 프로그램을 작성하시오.&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1136&quot; data-origin-height=&quot;996&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bComuc/btsOX9zAgQP/F8w6KQ6WnbnIfPddGrW4H1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bComuc/btsOX9zAgQP/F8w6KQ6WnbnIfPddGrW4H1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bComuc/btsOX9zAgQP/F8w6KQ6WnbnIfPddGrW4H1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbComuc%2FbtsOX9zAgQP%2FF8w6KQ6WnbnIfPddGrW4H1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;528&quot; data-origin-width=&quot;1136&quot; data-origin-height=&quot;996&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming/Python</category>
      <category>opencv-python</category>
      <category>opencv-python으로 배우는 영상처리 및 응용</category>
      <category>연습문제 풀이</category>
      <author>FoO__511</author>
      <guid isPermaLink="true">https://foo-511.tistory.com/19</guid>
      <comments>https://foo-511.tistory.com/19#entry19comment</comments>
      <pubDate>Mon, 30 Jun 2025 09:06:40 +0900</pubDate>
    </item>
    <item>
      <title>Windows/Kali Linux 듀얼부팅하기</title>
      <link>https://foo-511.tistory.com/18</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;며칠 전부터 Elastic stack 공부를 시작했다. 처음에는 리눅스 환경도 없고 귀찮으니 윈도우에서 깔짝이고 있었는데 하다보니 그래도 역시 리눅스가 편하겠단 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 내가 리눅스를 쓴 환경은 AWS EC2, WSL, VMWare, Raspberry pi 정도가 있는데, 다양한 이유로 그냥 잘 안쓰는 노트북에 리눅스를 깔기로 결심했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- AWS EC2 : 클라우드 비용 부담 / Free Tier 다씀 / 새 계정 파서 해도 Free Tier 사양 안좋음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- WSL :&amp;nbsp; 뭔가 억압된 느낌. 웬만한건 다 잘 돌아가긴 하는데 문제 터졌을 때 찾아보면 WSL라서 그렇다고 할 때의 좌절감...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- VMWare : 무겁다... 느리다... 켜고 끌 때 시간이 많이 든다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Raspberry pi : 램 8기가 짜리 라베파5가 있긴 한데 얘한테 달 모니터랑 키보드가 없어서 무조건 ssh로 붙어서 해야 하는게 귀찮다. 그리고 파워 연결을 해야하니 들고다니기 귀찮다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 노트북에 바로 리눅스를 깔려고 했는데 생각해보니 윈도우 깔린 노트북이 하나밖에 없었다. 그래서 멀티부팅을 하기로...&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OS를 칼리로 설정한 건 별 이유 없다. 리눅스 쓸 때 대체로 Ubuntu만 써와서 이참에 다른 OS를 써보고 싶었다. CentOS는 아무것도 모를 때 썼다가 스트레스 받았던 기억이 때문에 정이 안갔고, 요즘 핫하다는 Arch 리눅스는 너무 찐 같아서 (I use Arch btw) 넘어갔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 막상 써보니 너무 Ubuntu랑 똑같아서 (아무래도 같은 Debian 계열이니까요...) 다른걸 해볼까 싶다가도 이거 저거 많이 깔려 있어서 일단 만족이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고: 이 글은 일기쓰듯이 대충 쓴거라 참고해서 듀얼부팅을 하기에는 많이 부족하다. 혹시 시도해보고 싶으면 Google AI Studio에서 현재 노트북 환경과 원하는 OS 등 정보를 상세히 입력하고 안내를 받는 것을 추천한다. 그리고 멀티부팅 설정을 하는 동안 노트북으로 검색할 수 없으니 부팅 USB를 만들 수 있는 다른 PC가 준비된 상태에서 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;멀티부팅 하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 듀얼부팅하는 노트북은 삼성 갤럭시북이고, 윈도우가 깔린 상태다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Windows 준비 - 빠른 시작 끄기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Windows의 빠른 시작 기능을 꺼야 한다. 이 기능은 가끔 릴스에서 컴퓨터 느려지는 원인이라면서 바이럴 되는데, 대충 윈도우를 다 끄지 않고 최대 절전 모드처럼 만들어 부팅 속도를 높이는 기능이다. 이게 켜져 있으면 디스크가 잠겨 리눅스에서 접근하거나 부트로더 설치할 때 문제가 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 과정으로 끌 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Win+X &amp;gt; 전원 옵션 &amp;gt; 관련 설정 &amp;gt; 추가 전원 설정&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 왼쪽 메뉴에서 전원 단추 작동 설정 클릭&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 상단의 현재 사용할 수 없는 설정 변경 클릭&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 종료 설정 항목에서 &quot;빠른 시작 켜기(권장)&quot; 체크 해제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 변경 내용 저장 &amp;gt; PC 완전히 껐다 켜기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Bios 준비 - 보안부팅 끄기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;갤럭시북 바이오스는 F2 키를 누르면 들어갈 수 있다. 메인보드마다 바이오스 진입 키가 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;여기서 보안 부팅을 꺼야 한다. 갤럭시 북 바이오스는 심플하게 생겼는데, 메뉴 중 마지막인 부팅을 클릭하면 보안 부팅 설정 항목을 찾을 수 있다. 보안 부팅을 끄면 종료한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;3. 리눅스 설치 공간(파티션) 확보&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;듀얼 부팅 찾아보면 Win+X &amp;gt; 디스크관리 눌러서 볼륨 축소 하라는 글이 좀 있는데 난 그 방법으로 안됐다. 사용하지 않은 공간이 300기가나 남아있었는데 볼륨 축소가 제대로 되지 않는 문제가 있었다. defrag 이벤트를 확인 했을 때 pagefile.sys 때문에 걸린다고 하는데, 얘를 해결해도 여전히 볼륨 축소가 안됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하여튼 디스크 관리에서 볼륨 축소가 잘 되면 이 단계는 그냥 넘어가면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파티션 공간 최적화나 이거저거 다 해봤는데도 안되면 그냥 GParted 쓸 수 있는 부팅 USB 만들어서 C드라이브 밖에서 파티션 Resize 해줘야 한다. 난 Minitools랑 이거저거 해봤는데 애초에 C드라이브에서 OS가 돌아가는 와중에 서드파티 프로그램으로 C 드라이브 파티션을 나눌 수가 없다. (Minitools가 리사이즈 설정하고 재부팅하면 나눠준다고 하지만 사기다. 믿으면 안된다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 참고로 내가 한 방법은 좀 위험한 거 같으니 시도하기 전에 잘 백업하길...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Kali Live Iso나(여기 GParted 깔려있음) USB에서 GParted 돌릴 수 있게 부팅 USB를 만든다. 부팅 USB 만드는건 아래 참조. 아니면 Refus 부팅 USB 만들기 찾아보면 나온다. (iso 파일 선택 - GPT, UFFI, 나머지 그대로 - 시작 누르고 DD 선택)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. USB 꽂고 F2 키 연타해 BIOS 진입. 부트메뉴에서 UEFI: [USB 이름] 선택해 USB로 부팅&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Kali Live 이미지 사용했으면 앱 실행기에서 GParted 실행하고 C드라이브 리사이즈 하면 된다. GParted는 UI가 직관적이라 걍 감대로 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 재부팅&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;끄고 USB 빼고 재부팅하면 윈도우가 아직 잘 켜져야 하는데... 잘 안된다면 당신은 망했어요. 삼성 갤럭시북은 바이오스에서 공장초기화 지원해주던데 다른 메인보드는 잘 모르겠네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 리눅스 부팅 USB 준비&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 Refus 리눅스 부팅 USB 만들기 이렇게 검색하면 나온다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의할 점은&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 칼리 리눅스 공식 홈페이지에서 Installer 이미지 다운로드(Live 아님)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- USB는 8GB 이상 / 중요한 내용 있으면 백업&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 파티션 방식: GPT(대부분 최신 PC는 UEFI/GPT 방식)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 대상 시스템: UEFI (non CSM)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- DD 이미지 모드로 쓸 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 리눅스 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- USB 꽂은 상태에서 바이오스 진입해서 USB로 부팅&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Grapical Install 선택(CUI하고 싶으면 다른걸로)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 나머지 알아서... 난 영어로 먼저 시작하고 설치 끝나고 한국어로 변경함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;디스크 파티션 하기(중요)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파티션 설정하는 창 나오면 수동 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파티션 테이블에서 아까 만든 &quot;남은 공간&quot; 또는 &quot;FREE SPACE&quot; 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. EFI 시스템 파티션(매우 중요)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &quot;남은 공간&quot; 선택하고 새 파티션 만들기 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 크기: 500MB or 1GB&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 용도: EFI 시스템 파티션&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 파티션 설정 마치기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 스왑(Swap) 파티션&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 다시 남은 공간 선택 - 새 파티션 만들기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 크기: PC의 RAM 용량과 같거나 2배 크기(램 8기가면 swap 8기가 혹은 16기가. 다른 글에서는 램이 충분하면 swap을 안만들어도 된다고도 하던데.. 난 안전하게 그냥 만들었다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 용도: 스왑영역&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 파티션 설정 마치기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 루트(Root) 파티션&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 마지막으로 남은 공간 선택 - 새 파티션 만들기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 크기: 남은 공간 전부(자동입력됨)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 용도: Ext4 저널링 파일 시스템&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 마운트 위치: / (루트)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 파티션 설정 마치기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 부트로더 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 과정을 마치고 나머지 하다보면 알아서 부트로더가 깔린다. 만약 안깔리면 수동으로 해줘야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부트로더 설치창이 뜬다면 반드시 예를 누르고 부트로더를 설치할 장치로 드라이버 자체를 선택해야 한다.(/dev/sda 또는 /dev/nvme0n1 등. /dev/sda1이나 /dev/nvme0n1p4 이런 애들은 안된다. )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 완료되면 USB를 제거하고 재부팅한다. 아마 마지막에 다 됐다면서 Continue 누르라고 뜰텐데, 이거 누른 다음에 검은 화면에 글자들 지나가는거 보다가 재부팅 시작하는 타이밍에 맞춰서 USB를 빼면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 USB 빼는 타이밍이 중요한지는 모르겠는데 10시간 동안 포맷만 계속 하다가 마지막으로 성공했을 때 타이밍 맞춰 USB를 뺐기 때문에 미신적 믿음을 가지기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. 번외&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;칼리는 GUI로 xfce랑 착붙이라고 하는데, 나는 KDE Plasma를 선택했다. KDE가 예쁘다는 평이 많아서... 매우 만족 중이다. 컴퓨터 사양이 괜찮으면 설치 과정에서 꼭 KDE를 선택하기 바란다. (KDE가 아무리 무거워도 윈도우보다는 훨씬 가볍다. 그리고 최근에는 xfce랑 비슷하다는 것 같다. 나무위키가 그랬음 ㄱ-)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설치 후 환경 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공적으로 설치를 하고 나면 전원을 켰을 때 부트로더를 통해 윈도우를 켤지 리눅스를 켤지 선택할 수 있다. 안되면 BIOS에서 부트 메뉴를 확인해보고 GRUB 부트로더를 선택하면 된다. 만약 부팅 가능한 옵션이 하나라면 앞으로 긴 삽질이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 한글 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한글 설정은 fcitx 사용해서 했다. 이건 Kali(2025.2) + KDE Plasma(6.3.5) 조합에서 한거라 다른 조합은 또 다를 수도 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1332&quot; data-origin-height=&quot;1019&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HoVzS/btsOUYz63BO/lNX0efL0O0nAQ2UYvttNT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HoVzS/btsOUYz63BO/lNX0efL0O0nAQ2UYvttNT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HoVzS/btsOUYz63BO/lNX0efL0O0nAQ2UYvttNT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHoVzS%2FbtsOUYz63BO%2FlNX0efL0O0nAQ2UYvttNT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1332&quot; height=&quot;1019&quot; data-origin-width=&quot;1332&quot; data-origin-height=&quot;1019&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 시스템 설정 &amp;gt; 지역과 언어 에서 언어로 한국어를 선택하고 껐다 켠다. 여기까진 따로 설치할 건 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 설정하면 이제 기본언어가 한국어로 바뀌는데 입력은 여전히 영어만 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1751211028539&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt install fcitx-lib*
sudo apt install fcitx5-hangul fcitx5-configtool
fcitx-autostart&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한거 설치하고 껐다 켠다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;im-config로 fcitx5 선택(fcitx 아니고 fcitx5)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1332&quot; data-origin-height=&quot;1019&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WDAjl/btsOXltO0IK/0Z9okz277Kg5NcosMPNBZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WDAjl/btsOXltO0IK/0Z9okz277Kg5NcosMPNBZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WDAjl/btsOXltO0IK/0Z9okz277Kg5NcosMPNBZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWDAjl%2FbtsOXltO0IK%2F0Z9okz277Kg5NcosMPNBZ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1332&quot; height=&quot;1019&quot; data-origin-width=&quot;1332&quot; data-origin-height=&quot;1019&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 설정 &amp;gt; 키보드 &amp;gt; 가상 키보드에서 Fcitx 5를 선택한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1332&quot; data-origin-height=&quot;1019&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1w3o0/btsOXkaB358/fOTjzkrZnaplOhHskbp5wK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1w3o0/btsOXkaB358/fOTjzkrZnaplOhHskbp5wK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1w3o0/btsOXkaB358/fOTjzkrZnaplOhHskbp5wK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1w3o0%2FbtsOXkaB358%2FfOTjzkrZnaplOhHskbp5wK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1332&quot; height=&quot;1019&quot; data-origin-width=&quot;1332&quot; data-origin-height=&quot;1019&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 설정 &amp;gt; 키보드 &amp;gt; 키보드 &amp;gt; 우측 상단 키 바인딩 에서 한/영 키, 한자 키를 바인딩한다. 이거 안하면 R_Alt로 한/영 전환을 지정하게 되는데, 좀 늦게 한영 전환이 된다거나 안눌렀는데 지멋대로 스위칭된다거나 하는 렉이 걸린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cmd에서 fcitx5-configtool 들어가서 한/영 전환 설정을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1169&quot; data-origin-height=&quot;956&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w3u01/btsOWfHw5AJ/k1AkXOgvYrPAU63kaA9Gj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w3u01/btsOWfHw5AJ/k1AkXOgvYrPAU63kaA9Gj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w3u01/btsOWfHw5AJ/k1AkXOgvYrPAU63kaA9Gj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw3u01%2FbtsOWfHw5AJ%2Fk1AkXOgvYrPAU63kaA9Gj0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1169&quot; height=&quot;956&quot; data-origin-width=&quot;1169&quot; data-origin-height=&quot;956&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영어랑 한글 키보드를 넣어준다. 아마 처음에는 영어 키보드만 있을거다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1006&quot; data-origin-height=&quot;794&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5xw5c/btsOYcwfcun/Q5uFS8KhdFiJ1RsOvO0PSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5xw5c/btsOYcwfcun/Q5uFS8KhdFiJ1RsOvO0PSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5xw5c/btsOYcwfcun/Q5uFS8KhdFiJ1RsOvO0PSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5xw5c%2FbtsOYcwfcun%2FQ5uFS8KhdFiJ1RsOvO0PSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1006&quot; height=&quot;794&quot; data-origin-width=&quot;1006&quot; data-origin-height=&quot;794&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 탭에서 &quot;첫 번째 입력기와 현재 입력기 사이의 임시 전환&quot; 옆에 버튼을 누르고 한/영키를 누른다. 그럼 이제 키보드 설정 끝이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 시스템 상에서 한/영 전환이 잘 되는데, 크롬이나 electron 앱은 실행할 때 따로 플래그를 지정해줘야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1275&quot; data-origin-height=&quot;827&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Bfpkz/btsOWIilfiD/Sz9nIUZPTQiC6WeLhPT8k0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Bfpkz/btsOWIilfiD/Sz9nIUZPTQiC6WeLhPT8k0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Bfpkz/btsOWIilfiD/Sz9nIUZPTQiC6WeLhPT8k0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBfpkz%2FbtsOWIilfiD%2FSz9nIUZPTQiC6WeLhPT8k0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1275&quot; height=&quot;827&quot; data-origin-width=&quot;1275&quot; data-origin-height=&quot;827&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Chrome이랑 Chromium은 주소창에 &quot;chrome://flags&quot; 들어가서 &quot;Preferred Ozone platform&quot;으로 Wayland를 선택해주면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(이건 KDE 그래픽 플랫폼이 Wayland라서 그렇다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;카톡 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카카오톡은 데스크탑 앱은 윈도우와 맥밖에 지원 안한다. 리눅스에서 윈도우용 앱을 실행하려면 wine을 설치해야 한다. wine은 wine is not emulator인데, 말그대로 에뮬레이터는 아닌데 윈도우 앱을 실행할 수 있게 해주는 친구다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색하면 많이 나오니 설명 생략...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카톡을 설치하고 나니 한글도 설치할까 했는데 웹버전인 한컴독스도 있고, 카톡도 렉이 좀 걸리는데 한글은 얼마나 렉이 많이 걸릴까 싶어서 패스했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구글 드라이브 마운트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵시디언을 구글 마운트를 통해 여러 디바이스에서 사용하고 있어서 칼리에도 구글 드라이브 마운트를 하기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 별 생각없이 우분투랑 같은 계열이니까 우분투 구글 드라이브 마운트를 찾아봤는데, 칼리는 다른 방법을 써야 했다. 가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우분투는 PPA를 이용해 저장소를 추가하고 필요한 패키지를 다운로드 하는데 칼리에는 PPA가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 Kali Linux 기본 저장소에 포함되어 있는 rclone을 이용하면 훨씬 쉽게 구글 드라이브(그 외 다양한 원격 저장소를 지원함)를 마운트 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1751212690449&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt update
sudo apt install rclone -y

rclone config&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;rclone config을 하면 새 저장소를 정의하기 위해 이거저거 적어줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. n) New remote 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. name&amp;gt; 원격 저장소 별명 입력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Storage&amp;gt; 여러 원격 저장소 목록이 나타나는데 이 중 Google Drive 번호 입력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. client_id&amp;gt; 그냥 엔터&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. client_secret&amp;gt; 그냥 엔터&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. scope&amp;gt; 1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. root_folder_id&amp;gt; 그냥 엔터&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8. service_account_file&amp;gt; 그냥 엔터&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;9. Edit advanced config?&amp;gt; n 입력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10. use auto config?&amp;gt; y 입력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 질문에서 y를 누르면 자동으로 웹 브라우저가 열리면서 구글 로그인 창이 나타난다. 원하는 구글 계정으로 로그인하고 rclone 액세스를 허용하면 된다. Success! 메시지가 뜨면 브라우저를 닫고 다시 터미널로 돌아간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Configure this as a team drive?&amp;gt; n&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 설정이 맞는지 확인&amp;gt; 맞으면 y&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. current remotes: 목록에 방금 만든 저장소가 보이면 성공, q 눌러서 끝내기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 rclone이 구글 드라이브에 접근할 수 있게 되었고, 원하는 폴더에 구글 드라이브를 마운트 해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마운트용 폴더를 하나 만들고 다음 명령어로 마운트를 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1751213083291&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;rclone mount &amp;lt;아까 설정한 드라이브 이름&amp;gt;: &amp;lt;마운트할 경로&amp;gt; --vfs-cache-mode writes &amp;amp;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--vfs-cache-mode writes: 파일 읽기/쓰기 성능과 호환성을 향상시키는 옵션&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마운트 확인&lt;/p&gt;
&lt;pre id=&quot;code_1751213143392&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ls &amp;lt;마운트한 폴더&amp;gt;
df-h&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마운트 해제하고 싶으면&lt;/p&gt;
&lt;pre id=&quot;code_1751213186198&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fusermount -u &amp;lt;마운트경로&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;후기&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BmV2S/btsOVwwe5GA/B1Ka26X3afGkr7Prc1RAU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BmV2S/btsOVwwe5GA/B1Ka26X3afGkr7Prc1RAU0/img.png&quot; data-alt=&quot;ㅎㅎ&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BmV2S/btsOVwwe5GA/B1Ka26X3afGkr7Prc1RAU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBmV2S%2FbtsOVwwe5GA%2FB1Ka26X3afGkr7Prc1RAU0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ㅎㅎ&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항상 서버용 리눅스만 설치했었는데 패키지 빠방하게 설치된 데스크탑 리눅스를 얻어서 너무 편하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 할 수 있는게 많은데 윈도우보다 부팅도 빠르고 배터리 소모도 느리다. 켜자마자 팬 돌아가면서 이륙하던 노트북이 맞나 싶다..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 KDE가 너무 예쁘게 생겨서 맥북급 만족감이 있다. 또 자잘자잘하게 써먹을만한 유틸리티가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 괜히 칼리를 깐거 같다. 여기 있는 해킹툴들 쓸 일이 없는데... 환경 설정 하면서 찾아보니 Arch 글이 자주 나오던데 다음에는 Arch 리눅스를 세팅해보고 싶다. 아니면 KDE랑 Manjaro를 많이 쓴다고 하니 걔를 쓰는 것도 좋을 것 같다.&lt;/p&gt;</description>
      <category>kali linux</category>
      <category>kde plasma</category>
      <category>multi boot</category>
      <category>Windows</category>
      <category>듀얼부팅</category>
      <category>리눅스 카톡</category>
      <category>리눅스 한글</category>
      <category>멀티부팅</category>
      <author>FoO__511</author>
      <guid isPermaLink="true">https://foo-511.tistory.com/18</guid>
      <comments>https://foo-511.tistory.com/18#entry18comment</comments>
      <pubDate>Mon, 30 Jun 2025 01:19:11 +0900</pubDate>
    </item>
    <item>
      <title>[Jupyter extension] Jupyter 확장프로그램 개발 환경 구축</title>
      <link>https://foo-511.tistory.com/17</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jupyterlab.readthedocs.io/en/stable/extension/extension_tutorial.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식 문서&lt;/a&gt;를 따라 환경을 구축 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Conda 설치 및 가상환경 활성화&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://repo.anaconda.com/archive/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Conda 설치파일 레포지토리&lt;/a&gt;에서 설치 파일을 다운로드 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 환경은 우분투 22.04이기 때문에 최신 리눅스 버전을 curl로 땡겨왔다.&lt;/p&gt;
&lt;pre id=&quot;code_1719921959810&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl --output anaconda.sh https://repo.anaconda.com/archive/Anaconda3-2024.06-1-Linux-x86_64.sh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sha256sum로 파일 해시를 따서 다운로드 페이지에서 제공하는 해시와 비교해 검증할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1719922003997&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sha256sum anaconda.sh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해시가 일치한다면 무사히 파일이 다운로드 된 것이고 다운로드 된 sh 파일을 실행시키면 conda 설치가 시작된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 끝난 뒤에 conda init을 하면 필요한 설정이 ~/.bashrc 에 저장된다.&lt;/p&gt;
&lt;pre id=&quot;code_1719922118597&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;bash anaconda.sh
echo &quot;export PATH=~/anaconda3/bin:~/anaconda3/condabin:$PATH&quot; &amp;gt;&amp;gt; ~/.bashrc
source ~/.bashrc
conda init&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기화까지 끝난 뒤에 jupyter에서 제공하는 튜토리얼을 따라가면 된다. 가상환경을 만들고 활성화를 해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1719922308672&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;conda create -n jupyterlab-ext --override-channels --strict-channel-priority -c conda-forge -c nodefaults jupyterlab=4 nodejs=20 git copier=9 jinja2-time&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222; text-align: start;&quot;&gt;-n [env_name] : n은 name을 줄인 것으로 --name으로 사용할 수 있다. 여기서는 jupyterlab-ext가 가상환경 이름이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222; text-align: start;&quot;&gt;--override-channels : 기본 혹은 .condarc의 채널의 찾지 않는다. 이 옵션을 사용하면 -c(--channel)옵션이 필수적이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;-c [channel] : c는 channel을 줄인 것으로 --channel으로 사용할 수 있다. 패키지를 찾을 추가적인 채널을 정의한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;--strict-channel-priority : 동일한 이름의 패키지가 있을 경우, 채널의 우선순위가 높은 쪽의 패키지가 채택된다.&amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.conda.io/projects/conda/en/latest/commands/create.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;그 외 conda create 옵션들.&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경을 생성하면서 설정한 채널과 패키지들을 설치해야 되므로 환경생성에 시간이 걸린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성이 끝나면 아래 명령어로 가상환경을 활성화 할 수 있다. 활성화하면 터미널의 입력부 앞에 (env_name)이 생긴다.&lt;/p&gt;
&lt;pre id=&quot;code_1719923041538&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;conda init
source ~/.bashrc
conda activate jupyterlab-ext&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;conda deactivate로 가상환경을 비활성화 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Jupyterlab extension 환경구축&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원하는 확장프로그램의 이름으로 디렉터리를 만들고 안에서 copier으로 깃 레포를 복사해오자. 튜토리얼에서 제시하는 깃 명령어들은 생략하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1719923272170&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mkdir my_extension
cd my_extension/
copier copy --trust https://github.com/jupyterlab/extension-template .&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레포를 끌어온 뒤에 pip으로 필요한 패키지를 다운로드 한다. 두번째 명령어는 jupyter를 실행할 때 이 위치를 참고해 확장프로그램을 사용하도록 symlink를 생성해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1719923320933&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pip install -ve .
jupyter labextension develop --overwrite .&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 jupyter를 실행시키면 해당 경로의 확장프로그램이 설치된 채로 실행된다.&lt;/p&gt;
&lt;pre id=&quot;code_1719923616228&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;jupyter lab  --no-browser --port=8888 --ip=0.0.0.0&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원격 서버에서 작업하고 있기 때문에 위 옵션을 줘야 외부에서 접속할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서버 확장 프로그램&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 프론트엔드 확장프로그램 환경을 구축 하였다. 이 확장은 클라이언트 측, 즉 브라우저에서 jupyter notebook이나 lab이 어떻게 작동할지 작성한 것이다. 클라이언트 측에서 얻을 수 있는 jupyter 환경의 정보가 부족할 경우 server 확장 프로그램을 작성할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 확장 프로그램 환경을 구축하는 기본적인 과정은 &lt;a href=&quot;https://jupyter-server.readthedocs.io/en/latest/developers/extensions.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;매뉴얼&lt;/a&gt;에 자세하게 정리되어 있다. 클라이언트 측에서 사용할 새로운 api를 뚫으려면 서버 확장 프로그램을 작성하여야 하며, jupyter에서는 tornado라는 라이브러리를 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1725256788005&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from jupyter_server.base.handlers import JupyterHandler
from jupyter_server.extension.handler import ExtensionHandlerMixin
import tornado


class MyExtensionHandler(ExtensionHandlerMixin, JupyterHandler):
    @tornado.web.authenticated
    def get(self):
        ...

    @tornado.web.authenticated
    def post(self):
        ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 매뉴얼 페이지에서 제시하는 예시 코드이다. ExtensionHandlerMixin, JupyterHandler 등의 클래스를 상속받아 jupyter에서 제공하는 인터페이스를 활용할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jupyter 서버 확장 프로그램 개발에 제공되는 클래스의 인터페이스는 공식 문서(&lt;a href=&quot;https://jupyter-server.readthedocs.io/en/latest/api/jupyter_server.base.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;API Handler&lt;/a&gt; 등)에 정리되어 있다. 여러 코드를 활용하며 잘 모르는 클래스의 경우 위 페이지에 검색하여 인터페이스를 찾아 작업하거나, &lt;a href=&quot;https://discourse.jupyter.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;jupyter community&lt;/a&gt;에 있는 질문글을 참조하여 트러블 슈팅하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 자료가 많지 않아서 그런지(&lt;s&gt;FE 개발은 자료천국이라 굳이 비교하자면 자료가 별로 없는 느낌&lt;/s&gt;) 그렇게 해도 이상하게 독스랑 다른것 같다거나.. 뭔가 열심히 찾아보지만 야매 같다는 느낌을 지울 수 없다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 확장 프로그램 개발시 참고할만한 레포&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://github.com/jupyter-server/jupyter_server_terminals&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;jupyter_server_terminal&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://github.com/Quansight/jupyterlab-kernel-usage&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;jupyer-kernel-usage (커널 정보 읽어 소켓통신)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming/Python</category>
      <category>jupyter</category>
      <category>jupyter extension</category>
      <category>jupyter notebook</category>
      <author>FoO__511</author>
      <guid isPermaLink="true">https://foo-511.tistory.com/17</guid>
      <comments>https://foo-511.tistory.com/17#entry17comment</comments>
      <pubDate>Mon, 2 Sep 2024 15:21:53 +0900</pubDate>
    </item>
    <item>
      <title>2023~2024.5 회고</title>
      <link>https://foo-511.tistory.com/13</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;2023&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2023년은 심적으로 이슈가 있어서 뭘 했는지도 모르게 지나갔다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기록할만한 사건들은 이렇다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;한이음&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;두근두근 한국사 여행앱&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;client: Flutter&lt;/li&gt;
&lt;li&gt;servier: Nest.js&lt;/li&gt;
&lt;li&gt; vcs: gitlab&lt;/li&gt;
&lt;li&gt;infra: kubernetes on raspberry pi&lt;/li&gt;
&lt;li&gt;이용자가 유적지를 방문하면 유적지와 관련된 역사적 인물의 호감도가 오르고, 특정 임계점을 넘을 때마다 역사적 인물의 스토리가 풀리는 미연시(?) 게임. 한국사와 관련된 공공 데이터를 이용했다.&lt;/li&gt;
&lt;li&gt;창업 때문에 바빠서 프로젝트를 제대로 완료하지 못했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;쿠버네티스 스터디&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쿠버네티스 인 액션&lt;/li&gt;
&lt;li&gt;10장까지 보았는데 지금은 다 까먹었다. (기록의 중요성... 아이패드에 손필기 했었는데 블로그에 쓸걸 그랬다.)&lt;/li&gt;
&lt;li&gt;이 또한 창업 때문에 바빠서 제대로 완료하지 못했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;창업&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;BoB 그랑프리 수상을 기반으로 해당 주제로 창업을 이어가고 있다.&lt;/li&gt;
&lt;li&gt;많은 프론트 개발.. 노드 개발..&amp;nbsp;&lt;/li&gt;
&lt;li&gt;각종 행사 참여&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;그래프 구조 데이터를 인프라 구조도 형식으로 시각화하기 위한 미니 렌더링 엔진&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시뮬레이션 패키지에서 부모 자식 관계 및 커넥션 관계를 고려해 각 노드(svg rect 혹은 circle로 렌더링됨), 커넥션(svg line으로 렌더링 됨)의 좌표 및 크기를 시뮬레이션 한 뒤 렌더러 패키지에서 wrapping 된 d3를 이용해 svg로 렌더링한다. 시뮬레이션 패키지와 렌더러 패키지를 합치면 7000줄 정도 된다.&lt;/li&gt;
&lt;li&gt;hover, mouseenter, mouseleave등 이벤트에 대한 콜백 함수가 지원된다.&lt;/li&gt;
&lt;li&gt;Typescript를 이용하여 개발되었고 다양한 모듈 형식을 지원하도록 컴파일해서 웹 프로젝트 및 Node.js 프로젝트에 이식성이 좋다.&lt;/li&gt;
&lt;li&gt;React 프로젝트에서 사용하기 쉽도록 React component로 감싸고 사용자가 svg를 편리하게 조작(요약, 요소 숨김 등)할 수 있도록 sidebar 컴포넌트를 제공한다.&lt;/li&gt;
&lt;li&gt;verdaccio로 private npm을 구축해 lerna를 이용해 멀티패키지로 배포했다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;컴파일러는 vite를 사용했다. 각 패키지마다 vite.config.ts 파일이 있는데 더 깔끔하게 처리할 방법을 찾지 못했다.&lt;/li&gt;
&lt;li&gt;clean-package를 이용해 배포 전후 각 패키지의 package.json 파일을 알맞게 수정했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;489&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWgLcs/btsH0bZ6XI3/zwtolSijFVJPrGZfVjStK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWgLcs/btsH0bZ6XI3/zwtolSijFVJPrGZfVjStK1/img.png&quot; data-alt=&quot;그래프 데이터에서 인프라 구조도를 렌더링한 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWgLcs/btsH0bZ6XI3/zwtolSijFVJPrGZfVjStK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWgLcs%2FbtsH0bZ6XI3%2FzwtolSijFVJPrGZfVjStK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;489&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;489&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;그래프 데이터에서 인프라 구조도를 렌더링한 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;각종 창업 대회, 투자 지원 사업, 창업 행사 참여&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이쪽은 사실 사업화 팀원이 대부분 작업을 해서 나는 숟가락만 얹었다고 할 수 있다.&lt;/li&gt;
&lt;li&gt;학생창업300에 성장트랙으로 부스를 냈다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;많은 Next.js 프로젝트들&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Ant Design, Chakra-ui 등 다양한 UI 패키지를 사용해보다가 주 서비스는 Next.js + tailwindcss로 개발하는 것으로 했다. 다른 작은 서비스들도 점차 다른 의존성을 빼고 tailwindcss만 남기는게 목표다. (최근에 pandacss를 사용해보니 이걸 썼어도 좋았겠다는 생각이 들었다.&lt;/li&gt;
&lt;li&gt;랜딩 페이지를 많이 만들었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2024&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인프라 이사&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AWS 지원사업을 통해 크레딧을 받아 AWS로 인프라를 이전했다.&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;opentofu을 이용했고, 처음 ECS를 테라폼으로 구성하는 일을 맡아 며칠밤을 새가면서 했다. 자원 사용량에 따라 배포나 알림을 주는 부분은 제대로 구현하지 못했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;기존에 QA서버가 따로 없었는데 이참에 분리됐다. 프론트 QA 서버는 집에 굴러다니던 라즈베리파이를 들고와서 회사 공유기에 연결해 배포했다. &lt;s&gt;야생의 스타트업&lt;/s&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;GA4&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 서비스 및 랜딩 페이지에 GA4를 붙였다.&lt;/li&gt;
&lt;li&gt;처음에는 GTM에서 직접 모든 이벤트를 만들었으나, 개발자로서 직접 코드 수준에서 로깅을 처리할 수 있으면 그럴 필요가 없다는 걸 깨달았다. 이런...&amp;nbsp;&lt;/li&gt;
&lt;li&gt;사용자 행동 로깅 시에 생각보다 정책 고려사항이 좀 있는게 와닿았다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;서비스 독스 개발 - Docusource&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Docusource로 서비스 독스 개발. md 파일, mdx 파일을 통해 빠르게 독스 개발을 할 수 있다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;독스 개발보다 mdx 파일 곳곳에 컴포넌트를 넣고 영번역을 하는게 더 고역이었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;리포트 api 개발&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존에 있던 리포트 생성 api에서 디자인적으로 손 볼 부분과 페이지 넘김이 제대로 처리되지 않는 문제를 맡아 고치게 됐다.&lt;/li&gt;
&lt;li&gt;reportlab에서 제공하는 강력한 flowables 클래스들을 사용법을 사용해 빠르게 개발했다.&lt;/li&gt;
&lt;li&gt;아직 SQL 쿼리 최적화가 필요한 상태다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Storybook&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주 서비스가 점점 커지면서 개발 속도가 느려지는 문제가 생겨 storybook을 도입했다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;다행히 기존 컴포넌트들은 dumb component와 smart component로 명확히 구분되어 있어서 붙이기 쉬웠다.&lt;/li&gt;
&lt;li&gt;아직 데이터 mock은 처리하지 않았다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;백오피스 개발.. 시도?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;유저가 생길 때마다 유저 권한을 변경해야하는데, 이를 편리하게 하기 위한 백오피스를 개발하려고 했다.&lt;/li&gt;
&lt;li&gt;원하는 기능은 이미 다 만들었는데 관리자 인증 기능이 없어서 배포를 하지 못했다.&lt;/li&gt;
&lt;li&gt;pnpm을 이용해 client는 Next.js, server는 Nest.js으로 모노 레포 형식으로 개발했다.&lt;/li&gt;
&lt;li&gt;nestjs-typeorm-paginate를 이용해서 필터링과 페이지네이션을 한번에 하면 반환값 페이지가 일정하지 않은 문제가 발생했다. 깃에 가보니 주요 메인테이너가 한 명인듯 했고 다들 다른 자잘한 이슈를 들고와 흔들고 있었다. (ㅜㅜ)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;요즘 드는 생각&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트를 너무 많이 하긴 했는데.. 사실 프론트는 서비스 메인 로직과는 깊게 연관이 없으니 현타가 종종 온다. 그렇다고 재미없는건 아니다. 코드 조금 바뀔 때마다 화면이 바뀌는게 도파민 나오긴 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 프론트 자체보다는 이만큼 했으면 벌써 웹, 노드 생태계와 관련된 것들을 어느정도 알아야 할 것 같은데 스스로 성장이 부족하다고 느껴서 그런 것 같다. 바닥부터 웹팩 세팅을 해보던가, babel이나 swc를 분석해보던가 그런걸 해봐야지 해봐야지 하다가 결국에 json 상하차, UI 노가다를 하는 기분이다. UI 노가다를 할거면 애니메이션을 아주 깔쌈하게 뽑아내기라도 해야지 그것도 아니라.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 블로그라도 많이 써야겠다. 처리한 이슈들을 다 별 생각 없이 넘겨 버린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>회고</category>
      <author>FoO__511</author>
      <guid isPermaLink="true">https://foo-511.tistory.com/13</guid>
      <comments>https://foo-511.tistory.com/13#entry13comment</comments>
      <pubDate>Sun, 16 Jun 2024 18:51:42 +0900</pubDate>
    </item>
    <item>
      <title>Next.js 프로젝트에 GA4 붙이기(사용자 속성, 잠재고객, UTM)</title>
      <link>https://foo-511.tistory.com/12</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;극초기 스타텁 초기 멤버로 일하면서 벌써 4번째 랜딩 리뉴얼을 하고 있다. 첫 랜딩은 되는대로, 두번째도 되는대로, 세번째는 부스 직전에 3일만에 만들고 이번에는 개발하는데 일주일 정도 걸린 것 같다. 기존 랜딩페이지는 홈, 회사 소개, 제품 소개로 구성되어 있었고 이번에는 간결하게 원페이지에 제품 소개만 담는 방향으로 가기로 했다.&lt;br&gt;&amp;nbsp;&lt;br&gt;랜딩은 규모가 작아서 매번 다양한 UI 라이브러리를 써보고 있다. tailwindcss, chakra-ui를 거쳐 이번엔 panda css와 framer-motion의 조합으로 진행했다. 보통 Next.js를 기본으로 사용했는데 한번은 Svelt를 썼다가 팀원들의 항의에 다시 Next.js로 돌아왔다. 소기업에서 괜히 기술 스택 늘리지 맙시다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;개발이 끝났다면 이제 KPI 측정과 사용자 행동 추적을 위해 로깅을 해야 한다. 매뉴얼하게 기업 서버에 로깅하는 방편도 있겠지만 GA4라는 강력한 서비스가 있기 때문에 이를 사용하고 있다.&lt;br&gt;&amp;nbsp;&lt;br&gt;프론트 개발 및 배포에 관여할 권한이 없다면 GTM을 써서 상당한 노가다를 해야 했겠지만 나는 클라이언트 측에서 간편하게 gtag 함수만 사용하면 끝이다.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;2024년부터 크롬에서 부분적으로 third-party cookie 사용을 중단하기로 했다. &lt;br&gt;GA4에서 기본적으로 사용하는 쿠키('_ga', '_ga_&amp;lt;container_id&amp;gt;')는 first party cookie이므로 걱정할건 없다. &lt;br&gt;다만, 기존에 외부 쿠키를 이용해 사용자를 식별하는 기능을 사용했다면 그 분석은 이제 하지 못하게 된다. 대신 사용자 전용 광고를 위해 Protected Audience API를 대신 사용할 수 있다.&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;Next.js에 GA4 붙이기&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js 14의 앱라우터에서 GA4를 붙이는 방법은 두 가지가 있다.&amp;nbsp;&lt;/p&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;Next.js가 제공하는 @next/thrid-parties 패키지 사용하기&lt;/li&gt;&lt;li&gt;직접 스크립트 삽입하기&lt;/li&gt;&lt;/ol&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 전자를 사용했다가 이후 쿠키 정책 등 사용자 동의를 받을 때를 대비해 후자로 바꿨다. 자세한 내용은 아래에서..&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;[방법 1] Next.js가 제공하는 @next/thrid-parties 패키지 사용하기&lt;/h3&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://nextjs.org/docs/app/building-your-application/optimizing/third-party-libraries&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;공식문서&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;yarn add @next/third-parties&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;패키지 설치 후 app 디렉터리의 root layout 파일에 집어 넣기만 하면 된다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;import { GoogleAnalytics } from '@next/third-parties/google';

export default function RootLayout({
&amp;nbsp;&amp;nbsp;children,
}: Readonly&amp;lt;{
&amp;nbsp;&amp;nbsp;children: React.ReactNode;
}&amp;gt;) {
&amp;nbsp;&amp;nbsp;return (
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{children}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;GoogleAnalytics gaId={ENV.GOOGLE_TAG} /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/&amp;gt;
&amp;nbsp;&amp;nbsp;);
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;다만 이렇게 하면 처음 Google tag를 초기화 할 때 쿠키 동의에 대한 커스텀 설정을 하지 못하게 된다. gtag('config', &amp;lt;TAG&amp;gt;) 이후에 &lt;span style=&quot;color: #333333;&quot;&gt;consent(저장소, 쿠키 등 GA4 기능에 대한 동의)&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt; 설정을 하는 건 좀 찝찝해서 그냥 직접 스크립트를 삽입하기로 했다.&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;[방법 2] 직접 스크립트 삽입하기&lt;/span&gt;&lt;/h3&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;export default function GA4({ GOOGLE_TAG }: { GOOGLE_TAG: string }) {
&amp;nbsp;&amp;nbsp;return (
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Script
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;strategy=&quot;afterInteractive&quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;src={`https://www.googletagmanager.com/gtag/js?id=${GOOGLE_TAG}`}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Script
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;id=&quot;google-analytics&quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;strategy=&quot;afterInteractive&quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dangerouslySetInnerHTML={{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;__html: `
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;window.dataLayer = window.dataLayer || [];
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;function gtag(){dataLayer.push(arguments);}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;gtag('js', new Date());

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;gtag('consent', 'default', {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;'ad_storage': 'denied',
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;'ad_user_data': 'denied',
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;'ad_personalization': 'denied',
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;'analytics_storage': 'denied',
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;'region': ['ES', 'US-AK']
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;gtag('config', '${GOOGLE_TAG}');
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;`,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/&amp;gt;
&amp;nbsp;&amp;nbsp;);
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;직접 스크립트를 삽입하면 [GA4] - [데이터스트림] - [Google Tag] - [직접 설치] 에서 제공하는 스크립트에 위와 같이 원하는 설정을 할&amp;nbsp; 수 있다.&lt;br&gt;&amp;nbsp;&lt;br&gt;위처럼 consent 설정을 하면 해당 국가 코드에 있는 사용자들을 대상으로 GA4는 쿠키를 생성하지 않는다. &lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%9D%BC%EB%B0%98_%EB%8D%B0%EC%9D%B4%ED%84%B0_%EB%B3%B4%ED%98%B8_%EA%B7%9C%EC%B9%99&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;GDPR&lt;/span&gt;&lt;/a&gt;은 글로벌하게 쓰이는 정책이긴 일단은 EU 회원국에 국한되어 있기 때문에 해당 국가에 대해서만 막아둘 수 있다. 우리 서비스는 트래픽이 거의 없기 때문에 consent 설정도 일단은 제외해 뒀다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;460&quot; data-origin-height=&quot;142&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/06IMM/btsH1Gdk2FO/ZJRdNYebKJkIFwtTkgCTvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/06IMM/btsH1Gdk2FO/ZJRdNYebKJkIFwtTkgCTvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/06IMM/btsH1Gdk2FO/ZJRdNYebKJkIFwtTkgCTvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F06IMM%2FbtsH1Gdk2FO%2FZJRdNYebKJkIFwtTkgCTvK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;460&quot; height=&quot;142&quot; data-origin-width=&quot;460&quot; data-origin-height=&quot;142&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;consent 설정을 했다면 Cookie Script와 같은 쿠키 배너 서비스를 이용해서 간편하게 쿠키 동의 배너를 지정할 수 있다.&lt;br&gt;&amp;nbsp;&lt;br&gt;웬만한 쿠키 배너 서비스는 무료로 웹사이트를 스캔해 사용하는 쿠키, 로컬 스토리지 정보를 알아내고 어느 정책에 걸리는지 알려주고, 각 쿠키가 필수 쿠키인지, 성능 쿠키인지, 개별화 쿠키인지 등을 분석해준다. 국가별 배너 표시 여부 등 자세한 설정은 유료인 경우가 많다. (Cookie Script가 제일 UI가 예쁘다.)&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;362&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bY8m9m/btsH17Ir4Mb/mQu1hd1vgzKs67QKH3XDM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bY8m9m/btsH17Ir4Mb/mQu1hd1vgzKs67QKH3XDM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bY8m9m/btsH17Ir4Mb/mQu1hd1vgzKs67QKH3XDM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbY8m9m%2FbtsH17Ir4Mb%2FmQu1hd1vgzKs67QKH3XDM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;362&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;362&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;div style=&quot;background-color: #ffffff; color: #292929;&quot;&gt;
  &amp;nbsp; 
&lt;/div&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;컴포넌트에서 gtag 함수 호출하기&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;@next/third-parties 패키지를 사용한다면 해당 패키지의 sendGAEvent 함수를 호출해 간편하게 gtag 이벤트를 보낼 수 있다.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;이 글에서는 직접 gtag 함수를 이용할 것이다.&lt;br&gt;먼저 Typesciprt에서 gtag 함수 타입을 알 수 있도록 다음 패키지를 설치한다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;gtag 타입 패키지 설치&lt;/h4&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;yarn add -D @types/gtag.js&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;설치를 하고 나면 따로 호출할 필요없이 Gtag 타입을 이용할 수 있다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;gtag 함수 이용&lt;/h3&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;// client에서만 gtag를 호출하도록 함
export const safeGtag: Gtag.Gtag = &amp;lt;Command extends keyof Gtag.GtagCommands,&amp;gt;(command: Command, ...args: Gtag.GtagCommands[Command]) =&amp;gt; {
&amp;nbsp;&amp;nbsp;if (window?.gtag &amp;amp;&amp;amp; typeof window.gtag === 'function')
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return window.gtag(command, ...args)
&amp;nbsp;&amp;nbsp;else return
}

// 안전한 gtag 함수를 통해 이벤트 호출
export const gtagSendEvent = ({ action, ...rest }: GTagEvent &amp;amp; { [k: string]: string | number | undefined }): void =&amp;gt; {
&amp;nbsp;&amp;nbsp;safeGtag(&quot;event&quot;, action, {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...rest
&amp;nbsp;&amp;nbsp;});
};

// 사용자 속성을 GA4 콘솔에서 직접 지정해야 하기 때문에 관리가 편하도록 enum으로 정의
export enum UserProperty {
&amp;nbsp;&amp;nbsp;ViewStatus = &quot;view_status&quot;,
&amp;nbsp;&amp;nbsp;IsVisiter = &quot;is_visitor&quot;,
&amp;nbsp;&amp;nbsp;IsContacter = &quot;is_contactor&quot;,
}

const gtagSetUserProperty = (key: UserProperty, value: string) =&amp;gt; {
&amp;nbsp;&amp;nbsp;safeGtag('set', 'user_properties', {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[key]: value
&amp;nbsp;&amp;nbsp;});
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;먼저 윈도우가 있을 때만 gtag함수를 호출하도록 감싸준 뒤 해당 함수를 이용해 gtag를 제한적으로 이용할 수 있는 다른 함수들을 만든다.&lt;br&gt;&amp;nbsp;&lt;br&gt;특히 맞춤 측정기준(이벤트 매개변수, 사용자 속성)이나 주요이벤트(전 전환) 등은 GA4콘솔에서 다시 직접 설정해야 하기 때문에 따로 모아 관리하는게 좋다.&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;랜딩 페이지에서 로깅하는 정보는 다음과 같다.&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
 &lt;li&gt;사용자 속성 
  &lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
   &lt;li&gt;페이지 컨텐츠를 어디까지 봤는지&lt;/li&gt; 
   &lt;li&gt;서비스 바로가기 버튼 클릭 여부&lt;/li&gt; 
   &lt;li&gt;서비스 문의하기 버튼 클릭 여부&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
 &lt;li&gt;이벤트 
  &lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
   &lt;li&gt;각 섹션 컴포넌트가 view port에 로드될 때&lt;/li&gt; 
   &lt;li&gt;인터랙티브 컴포넌트에 참여했을 때(hover 등)&lt;/li&gt; 
   &lt;li&gt;버튼 혹은 링크를 클릭했을 때(해당 요소의 위치도 함께 기록)&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;GA4 콘솔에서 사용자 속성 및 잠재고객 설정하기&lt;/h2&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용자 속성 설정 (Aka. 맞춤 측정기준 설정)&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;위에 정의된 함수를 사용하여 사용자 속성을 GA4에 전송해도 그 정보는 보고서에 남지 않는다. 커스텀 사용자 속성을 이용하려면 맞춤 측정기준을 설정해야 한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;[관리]-[속성 설정]-[데이터 표시]-[맞춤 정의]에서 새로운 맞춤 기준을 설정할 수 있다. 맞춤 측정기준 만들기 버튼을 클릭한 후 위에서 사용한 사용자 속성을 그대로 입력하여 맞춤 측정기준을 만들 수 있다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1142&quot; data-origin-height=&quot;562&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/odh2H/btsH0nzbJ1J/OKAb542qmhpKXRPsCAZwB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/odh2H/btsH0nzbJ1J/OKAb542qmhpKXRPsCAZwB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/odh2H/btsH0nzbJ1J/OKAb542qmhpKXRPsCAZwB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fodh2H%2FbtsH0nzbJ1J%2FOKAb542qmhpKXRPsCAZwB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;246&quot; data-origin-width=&quot;1142&quot; data-origin-height=&quot;562&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;맞춤 측정기준을 만들고 바로 보고서의 [비교]에서 사용할 수도 있지만, 해당 측정기준을 기반으로 잠재고객을 만드는 편이 더 편리하다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;잠재고객 설정&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;[관리]-[속성 설정]-[데이터 표시]-[구축]에서 잠재고객을 설정할 수 있다.&lt;br&gt;&amp;nbsp;&lt;br&gt;[새 잠재고객 만들기]-[맞춤 잠재고객 만들기]를 통해 원하는 사용자 속성을 마음대로 조합해 잠재고객을 만들 수 있다.&lt;br&gt;&amp;nbsp;&lt;br&gt;[조건 선택]-[맞춤]에서 위에서 만든 맞춤 측정기준을 선택하고 필터를 걸면 된다.&lt;/p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RrNfs/btsH0pX20b2/BbWZiruJBSIlELl41LoZ50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RrNfs/btsH0pX20b2/BbWZiruJBSIlELl41LoZ50/img.png&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;473&quot; style=&quot;width: 43.7724%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RrNfs/btsH0pX20b2/BbWZiruJBSIlELl41LoZ50/img.png&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRrNfs%2FbtsH0pX20b2%2FBbWZiruJBSIlELl41LoZ50%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;473&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/z1M1N/btsHZ984c6d/phBkFS29ljTwC9kJo7k0SK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/z1M1N/btsHZ984c6d/phBkFS29ljTwC9kJo7k0SK/img.png&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;376&quot; style=&quot;width: 55.0648%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/z1M1N/btsHZ984c6d/phBkFS29ljTwC9kJo7k0SK/img.png&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fz1M1N%2FbtsHZ984c6d%2FphBkFS29ljTwC9kJo7k0SK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;376&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;이렇게 잠재고객을 만들어두면 [보고서]-[사용자]-[사용자 속성]-[잠재고객]에서 각 고객별 수집 데이터를 확인하거나, 다른 보고서에서 비교를 통해 잠재고객 별 통계를 볼 수 있다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;UTM 파라미터&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Urchin Tracking Module&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #202122;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #202122;&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;b&gt;UTM&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #202122;&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #202122;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;parameters&lt;/b&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;GA에서 사용하는 사용자 추적을 위한 쿼리 파라미터이다. GET 쿼리에서 utm_&amp;lt;variable_name&amp;gt; 형태로 쓰이며, GA에서 기본적으로 제공하는 변수들은 &lt;a href=&quot;https://support.google.com/analytics/answer/10917952?hl=ko#zippy=%2Cin-this-article%2C%EC%9D%B4-%EB%8F%84%EC%9B%80%EB%A7%90%EC%97%90%EC%84%9C%EB%8A%94-%EB%8B%A4%EC%9D%8C-%EB%82%B4%EC%9A%A9%EC%9D%84-%EB%8B%A4%EB%A3%B9%EB%8B%88%EB%8B%A4&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;공식 문서&lt;/span&gt;&lt;/a&gt;에 자세히 나와있다.&lt;/p&gt;&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;width: 50%;&quot;&gt;utm_source&lt;/td&gt;&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #1f1f1f;&quot;&gt;리퍼러(예: Google, 뉴스레터4, 빌보드)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 50%;&quot;&gt;utm_medium&lt;/td&gt;&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #1f1f1f;&quot;&gt;마케팅 매체(예: CPC, 배너, 이메일)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 50%;&quot;&gt;utm_campaign&lt;/td&gt;&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #1f1f1f;&quot;&gt;제품, 슬로건, 프로모션 코드(예: spring_sale)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;위와 같은 파라미터들은 따로 설정을 하지 않아도 사용자가 UTM 파라미터가 있는 페이지로 접속하기만 하면 매개변수로 기록된다.&lt;br&gt;&amp;nbsp;&lt;br&gt;아래는 향상된 측정을 사용할 경우 자동으로 기록되는 이벤트 page_view의 매개변수로 UTM 파라미터가 들어간 모습이다. 앞의 utm_ 프리픽스는 빠지고 medium, source, campaign만 기록 된다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;272&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bonpg5/btsHZS7EG0U/zO0zszF1oRwte812rKTe91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bonpg5/btsHZS7EG0U/zO0zszF1oRwte812rKTe91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bonpg5/btsHZS7EG0U/zO0zszF1oRwte812rKTe91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbonpg5%2FbtsHZS7EG0U%2FzO0zszF1oRwte812rKTe91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;272&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;272&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;GA4를 붙이고 이벤트를 수집하는 것까진 얼마 걸리지 않았는데 GDPR 등 쿠키 정책과 쿠키 배너 서비스를 알아보는데 많은 시간을 소요했다. 오랫동안 찾아보긴 했지만 트래픽이 조금 더 생기기 전까진 GDPR을 준수하는 것은 보류하기로 했다.&lt;/p&gt;</description>
      <category>Programming/FE</category>
      <category>GA4</category>
      <category>google analytics</category>
      <category>nextjs</category>
      <category>utm parameter</category>
      <category>사용자 속성</category>
      <category>잠재고객</category>
      <author>FoO__511</author>
      <guid isPermaLink="true">https://foo-511.tistory.com/12</guid>
      <comments>https://foo-511.tistory.com/12#entry12comment</comments>
      <pubDate>Sun, 16 Jun 2024 16:53:21 +0900</pubDate>
    </item>
    <item>
      <title>개방 시스템 상호 연결(OSI, Open System Interconnection)</title>
      <link>https://foo-511.tistory.com/8</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;개방 시스템(Open System)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기반 구조에 관계없이 서로 다른 두 시스템 간 통신을 가능하게 하는 프로토콜의 집합&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개방 시스템 상호 연결(OSI) 모델&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1983년 &lt;b&gt;ISO&lt;/b&gt;(International Standardization Organization) 표준화 기구에서 개발 시작.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터 통신 분야에서 &lt;b&gt;상호운용성&lt;/b&gt;을 확보하기 위한 다양한 표준에 대한 &lt;b&gt;국제적인 표준 정의&lt;/b&gt;를 위한 기본 골격.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;융통성&lt;/b&gt;을 부여. 특정 계층만 업그레이드하여 교체할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 계층별로 여러가지의 프로토콜이 존재함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;OSI_7_layer.png&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IzYEO/btrv6Yk3ns5/d7YbtR7EOpcamS5ZNvtJN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IzYEO/btrv6Yk3ns5/d7YbtR7EOpcamS5ZNvtJN0/img.png&quot; data-alt=&quot;OSI 7계층 모델&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IzYEO/btrv6Yk3ns5/d7YbtR7EOpcamS5ZNvtJN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIzYEO%2Fbtrv6Yk3ns5%2Fd7YbtR7EOpcamS5ZNvtJN0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;467&quot; height=&quot;467&quot; data-filename=&quot;OSI_7_layer.png&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;OSI 7계층 모델&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;요약&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 156px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 16px;&quot;&gt;
&lt;td style=&quot;width: 18.8373%; height: 16px; text-align: left;&quot;&gt;Layer&lt;/td&gt;
&lt;td style=&quot;width: 11.8603%; height: 16px; text-align: left;&quot;&gt;데이터 단위&lt;/td&gt;
&lt;td style=&quot;width: 13.8372%; height: 16px; text-align: left;&quot;&gt;장비&lt;/td&gt;
&lt;td style=&quot;width: 40.2908%; height: 16px; text-align: left;&quot;&gt;특징&lt;/td&gt;
&lt;td style=&quot;width: 15.1744%; height: 16px; text-align: left;&quot;&gt;예&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 18.8373%; height: 20px; text-align: left;&quot;&gt;&lt;span&gt;[L7]&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Application&lt;/td&gt;
&lt;td style=&quot;width: 11.8603%; height: 20px; text-align: left;&quot;&gt;data&lt;/td&gt;
&lt;td style=&quot;width: 13.8372%; height: 20px; text-align: left;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 40.2908%; height: 20px; text-align: left;&quot;&gt;사용자에게 서비스 제공.&amp;nbsp;&lt;br /&gt;파일 전송, 접근, 관리. 문서, 메시지 교환&lt;/td&gt;
&lt;td style=&quot;width: 15.1744%; height: 20px; text-align: left;&quot;&gt;전자메일(SMTP), 가상단말, 파일 송수신(FTP), 웹(HTTP)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 18.8373%; height: 20px; text-align: left;&quot;&gt;&lt;span&gt;[L6]&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Presentation&lt;/td&gt;
&lt;td style=&quot;width: 11.8603%; height: 20px; text-align: left;&quot;&gt;data&lt;/td&gt;
&lt;td style=&quot;width: 13.8372%; height: 20px; text-align: left;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 40.2908%; height: 20px; text-align: left;&quot;&gt;데이터 표현 방식 변환, 암호화, 압축, 코드변환, 가상 터미널 규약&lt;/td&gt;
&lt;td style=&quot;width: 15.1744%; height: 20px; text-align: left;&quot;&gt;ASN.1, BER(Basic Encoding Rule)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 18.8373%; height: 20px; text-align: left;&quot;&gt;&lt;span&gt;[L5]&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Session&lt;/td&gt;
&lt;td style=&quot;width: 11.8603%; height: 20px; text-align: left;&quot;&gt;data&lt;/td&gt;
&lt;td style=&quot;width: 13.8372%; height: 20px; text-align: left;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 40.2908%; height: 20px; text-align: left;&quot;&gt;통신 프로세스 간 대화제어 및 동기화, 대화 단위별 그룹화(check point, synchronization)&lt;/td&gt;
&lt;td style=&quot;width: 15.1744%; height: 20px; text-align: left;&quot;&gt;소켓, RPC, NFS, SMB, NetBIOS, SQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 18.8373%; height: 20px; text-align: left;&quot;&gt;&lt;span&gt;[L4]&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Transport&lt;/td&gt;
&lt;td style=&quot;width: 11.8603%; height: 20px; text-align: left;&quot;&gt;segments&lt;/td&gt;
&lt;td style=&quot;width: 13.8372%; height: 20px; text-align: left;&quot;&gt;L4 스위치&lt;/td&gt;
&lt;td style=&quot;width: 40.2908%; height: 20px; text-align: left;&quot;&gt;종단까지 온 패킷을 종단 내에서 최종 수신 프로세스로 전달(포트주소)&lt;br /&gt;투명한 전송: 종단까지 오는 과정 고려x&lt;br /&gt;&lt;br /&gt;연결 관리, 에러제어, 데이터 분리, 흐름제어, 다중화, 역다중화&lt;/td&gt;
&lt;td style=&quot;width: 15.1744%; height: 20px; text-align: left;&quot;&gt;TCP, UDP, SCTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 18.8373%; height: 20px; text-align: left;&quot;&gt;&lt;span&gt;[L3]&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Network&lt;/td&gt;
&lt;td style=&quot;width: 11.8603%; height: 20px; text-align: left;&quot;&gt;packets&lt;/td&gt;
&lt;td style=&quot;width: 13.8372%; height: 20px; text-align: left;&quot;&gt;L3 스위치, 라우터&lt;/td&gt;
&lt;td style=&quot;width: 40.2908%; height: 20px; text-align: left;&quot;&gt;최종 목적지까지 패킷 전달.&lt;br /&gt;routing(경로배정), 주소, 호 설정 및 해지.&lt;/td&gt;
&lt;td style=&quot;width: 15.1744%; height: 20px; text-align: left;&quot;&gt;IP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 18.8373%; height: 20px; text-align: left;&quot;&gt;&lt;span&gt;[L2]&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Data Link&lt;/td&gt;
&lt;td style=&quot;width: 11.8603%; height: 20px; text-align: left;&quot;&gt;frame&lt;/td&gt;
&lt;td style=&quot;width: 13.8372%; height: 20px; text-align: left;&quot;&gt;L2 스위치&lt;/td&gt;
&lt;td style=&quot;width: 40.2908%; height: 20px; text-align: left;&quot;&gt;노드와 노드 사이 데이터 전달.&lt;br /&gt;프레임화, 데이터 투명성, 오류제어. &lt;br /&gt;-&amp;gt; 물리계층+신뢰성&lt;br /&gt;서브레이어: LLC, MAC&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 15.1744%; height: 20px; text-align: left;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 18.8373%; height: 20px; text-align: left;&quot;&gt;[L1] Physical&lt;/td&gt;
&lt;td style=&quot;width: 11.8603%; height: 20px; text-align: left;&quot;&gt;bits&lt;/td&gt;
&lt;td style=&quot;width: 13.8372%; height: 20px; text-align: left;&quot;&gt;리피터, 허브&lt;/td&gt;
&lt;td style=&quot;width: 40.2908%; height: 20px; text-align: left;&quot;&gt;기계적, 전기적 통신망 인터페이스 정의. &lt;br /&gt;신호인코딩/디코딩(선로부호화), 비트 동기&lt;br /&gt;전송매체 규격 정의(토폴로지 포함)&lt;/td&gt;
&lt;td style=&quot;width: 15.1744%; height: 20px; text-align: left;&quot;&gt;EIA RS-232C, V.24, X.21, V.35&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;계층별 기능&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;1계층(L1) : 물리계층(Physical Layer)&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기계적, 전기적 통신망 인터페이스 정의&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단말 기기와 전송매체 간 인터페이스 정의. &lt;b&gt;물리적인 비트 전송&lt;/b&gt; 수단 제공.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 단위 : &lt;b&gt;bit&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;신호 인코딩/디코딩&lt;/b&gt; : 선로부호화(선로에 맞게 신호를 전기적 펄스열로 변환)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비트 동기&lt;/b&gt; : 프리엠블(각 프레임 맨 앞에 붙이는 영역) 처리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;물리적 회선 관리&lt;/b&gt; : DTE(Data Terminal Equipment, 데이터 단말장치)와 DCE(Data Communication Equipment, 데이터 회선종단장치)간에 성립되는 물리적 회선에 대한 작동, 유지, 중지 등 규정. 점대점 혹은 다중점.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;물리계층 인터페이스 구현&lt;/b&gt; : 상호 연결되는 장치의 논리적, 물리적, 전기적 특성이 정의되어야 함.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;전송방식 규정&lt;/b&gt; : 2개 장치 간 전송방향 규정. (단방향, 반이중, 전이중)&lt;/li&gt;
&lt;li&gt;오류제어는 하지 않음.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;2계층(L2): 데이터링크 계층(Data Link Layer)&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 링크 제어(프레임화, 데이터 투명성, 오류 제어)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;잡음이 있는 인접 노드 간&lt;/b&gt; 물리적 회선을 상위 계층인 네트워크 계층이 &lt;b&gt;신뢰적&lt;/b&gt;으로 사용할 수 있도록 &lt;b&gt;전송 에러가 없는&lt;/b&gt; 통신 채널로 변환시키는 계층.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인접한 기기 사이&lt;/b&gt;의 통신 관리, 신뢰도 &lt;b&gt;낮은 전송로를 신뢰도 높은 전송로로&lt;/b&gt; 전환.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 단위 : &lt;b&gt;frame&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;3계층 프로토콜 전송/운반/전달&lt;/li&gt;
&lt;li&gt;&lt;b&gt;프레이밍(Framing)&lt;/b&gt; : 데이터를 frame으로 그룹. =물리계층에서 제공되는 Bit Pipe를 패킷화(또는 프레임)된 링크로 변환. 그냥 비트들을 패킷이나 프레임으로 만든다는 뜻. 송신자 및 수신자 &lt;b&gt;주소(MAC)정보를 프레임 헤더&lt;/b&gt;에 추가.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;흐름제어(Flow Control)&lt;/b&gt; : 보내는 측과 받는 측 사이 속도차를 보상&lt;/li&gt;
&lt;li&gt;&lt;b&gt;에러제어(Error Control)&lt;/b&gt; : 전송 오류 검출, 수정 혹은 재전송. (송신측에서 타이머와 ACK신호에 의해 전송에러 감지)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;매체접근제어(Media Access Control)&lt;/b&gt; : 둘 이상의 장치가 동시에 공유 매체에 접근할 시 발생하는 충돌(Collision)문제 해결&lt;/li&gt;
&lt;li&gt;&lt;b&gt;순서화(Sequencing)&lt;/b&gt; : 패킷, ACK 신호를 잘못 혼동하는 것을 피하기 위해 패킷과 ACK에 일련번호(Sequence Number)부여&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터링크 연결 관리(Data Link Connection Management)&lt;/b&gt; : 네트워크 개체간 데이터 링크 설정, 유지, 단락 및 데이터 전송 제어.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터링크 내 노드 간 식별성 제공&lt;/b&gt; : MAC&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IEEE 802표준에서는 LAN상 데이터링크 계층을 2개의 부계층으로 나눔.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;LLC 부계층&lt;/li&gt;
&lt;li&gt;MAC 부계층&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;3계층(L3): 망계층(Network Layer)&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로배정. 주소, 호 설정 및 해지.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷의 IP가 여기 속함. 인터넷은 3계층에서 IP, 4계층에서 TCP프로토콜을 채택.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 단위 : &lt;b&gt;packet&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;경로제어(Routing)&lt;/b&gt; : 패킷이 최종 목적지에 전달될 수 있도록 경로 지정 메커니즘 제공.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;투명성&lt;/b&gt; : 상위 계층인 전송계층에서 망의 형태를 의식하지 않도록 투명하게 처리.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;논리적 주소체계&lt;/b&gt; : 라우팅과 관련된 주소 지정 등 주소체계 정립. IP주소 등. &lt;b&gt;상위 계층에서 받은 패킷&lt;/b&gt;에 송신측 및 수신측 &lt;b&gt;논리 주소를 헤더에 추가&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;연결지향성 및 비연결성 서비스 제공&lt;/b&gt; : 망 계층은 전송계층에게 연결지향성 및 비연결성 서비스 제공.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;연결지향 서비스&lt;/b&gt;&lt;br /&gt;두 개체 간 1 이상의 메시지들이 연결상태(세션)를 유지하며 데이터 교환이 가능한 서비스&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;비연결성&lt;/b&gt;&lt;br /&gt;호 설정 절타 없이 단순히 목적지를 향해 목적지 주소를 담은 패킷 전송. 패킷이 손실되면 수신측에서 문제 검출해 송신측에 재전송 요구. 패킷은 Datagram이라는 독립적인 단위로 전송.&lt;br /&gt;예) 망계층의 IP 프로토콜, 전송계층의 UDP 프로토콜&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 계층을 지났으면 통신로를 통해 &lt;b&gt;멀리서 길 찾아오는 일&lt;/b&gt;은 끝났다고 보면 됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상위 계층들은 하위계층을 고려하지 않고(투명하게) 통신&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;4계층(L4): 전송계층(Transport Layer)&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;양단간 어떤 종류의 망이 사용되었는지 의식x&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;가장 복잡하고 핵심적&lt;/b&gt;인 계층. 소프트웨어적인 상위 3개 계층과 하드웨어적인 하위 3개 계층 사이의 중간 성격.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;네트워크 계층에서 얻은 서비스 형태&lt;/b&gt;에 따라 &lt;b&gt;전송계층 프로토콜&lt;/b&gt;의 &lt;b&gt;크기&lt;/b&gt;와 &lt;b&gt;복잡성&lt;/b&gt;이 결정.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;망계층&lt;/b&gt;에서 가상회선 능력 및 &lt;b&gt;신뢰할 수 있는 통신 지원&lt;/b&gt;이 있으면 &lt;b&gt;전송계층 기능 최소&lt;/b&gt;로 축소.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대일 경우 &lt;b&gt;전송계층에서 에러감지, 회복&lt;/b&gt; 등 담당. (TCP)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;가상회선&lt;/b&gt;&lt;br /&gt;패킷교환망에서 2개 이산의 노드 간에 구성되는 &lt;b&gt;가상의 종단간 연결&lt;/b&gt;.&lt;br /&gt;패킷 전송 전 &lt;b&gt;논리적 경로&lt;/b&gt;를 구성 -&amp;gt; 이 경로로만 데이터가 흐르게 됨.&amp;nbsp;&lt;br /&gt;&lt;b&gt;실제 패킷은 물리적으로 다른 경로&lt;/b&gt;로 전달될 수 있음.&lt;br /&gt;연결 내의 각 패킷은 자신이 어느 가상회선에 속하는지 알리는 &lt;b&gt;가상회선 식별자&lt;/b&gt;를 가짐.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 단위 :&lt;span&gt;&amp;nbsp;&lt;b&gt;segments&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네트워크가 아닌 호스트 내에서 구동된 프로세스 사이의 연결(&lt;b&gt;End-to-End&lt;/b&gt;) 확립: &lt;b&gt;host-to-host&lt;/b&gt;에서 &lt;b&gt;process-to-process&lt;/b&gt;로 바꾸어줌. 양 호스트 종단간 응용 프로세스 상호간 통신 지원.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;투명한 전송&lt;/b&gt; : 망 계층이나 세션계층에서 사용하는 제어방법과 내용에 관계없이 정보가 바뀜없이 투명하게 전송. 경로선택이나 중계기능에 관여x 오로지 end-to-end 데이터의 투명한 양방향 전송 기능 제공.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;두 종단 프로테스 간 성립된 세션 제어&lt;/b&gt; : 세션을 가지고 있는 두 이용자 간 전송을 위해 종단 간 제어&lt;/li&gt;
&lt;li&gt;&lt;b&gt;신뢰적 전송 지원&lt;/b&gt; : 어떤 망이 어떻게 연결되었든 간에 관계없이 정보를 상대 단말에 전달. 논리 회선(세션, 가상회선 등) 가능.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;발신지 대 목적지 수준에서 오류제어, 흐름제어&amp;nbsp;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;다중화/역다중화 지원&lt;/b&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;지연에 따른 왜곡 및 대역폭 부족 일부 보상&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;다중화(Multiplexing, MUX)&lt;/b&gt;&lt;br /&gt;설비를 더 효율적으로 이용하기 위해 하나의 설비에 여러 신호를 함께 결합시켜 보냄.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;역다중화(Demultiplexing, DeMUX)&lt;/b&gt;&lt;br /&gt;다중화로 송신한 다중 신호를 수신단에서 분리하여 보냄.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;5계층(L5): 세션계층(Session Layer)&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종단 호스트 프로세스 간 &lt;b&gt;세션을 관리&lt;/b&gt;하는데 필요한 여러 기능 제공.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;네트워크 대화 제어자(Dialog Controller)&lt;/b&gt;로서 &lt;b&gt;통신 시스템간 상호 대화(세션)&lt;/b&gt; 설정.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 단위 :&lt;b&gt;&lt;span&gt; data&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;세션 관리(session manage)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;대화 관리(dialoge manage)&lt;/b&gt; : 토큰을 사용해 대화 관리.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;다중화(multiplexing)&lt;/b&gt; : 여러 세션들이 효율을 위해 1개의 같은 전송계층 접속 사용 가능. 혹은 속도를 위해 1개 세션이 다수의 전송계층 접속을 사용할 수 있음.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;대화 단위별 그룹화(check point, synchronization)&lt;/b&gt; : 전송시 &lt;b&gt;점검점, 동기점&lt;/b&gt; 등을 삽입함으로써 메시지를 대화 단위로 그룹화. 에러 발생하면 중단된 대화 단위부터 전송 재시작.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터의 범주화 교환&lt;/b&gt; : OSI는 데이터를 4가지 범주로 구분(정보 데이터, 급송 데이터, 제어 데이터, 세션 파라미터 협상에 사용되는 데이터)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소켓, RPC, NFS, SMB, NetBIOS&lt;/li&gt;
&lt;li&gt;SQL&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;6계층(L6): 표현계층(Presentation Layer)&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 상 여러 &lt;b&gt;이기종 시스템&lt;/b&gt;들이 저마다 &lt;b&gt;다른 데이터 표현 방식&lt;/b&gt; 사용 -&amp;gt; &lt;b&gt;하나의 통일된 구문 형식으로 변환&lt;/b&gt;하는 역할.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 단위 :&lt;b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;data&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;응용계층의 다양한 표현양식(Syntax)을 공통 형식으로 변환&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;암호화(Encryption) 및 복호화(Decryption)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;압축(Compression)&amp;nbsp;&lt;/b&gt;: 전달되는 비트수 최소화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;코드 변환&lt;/b&gt; : 서로 상이한 형태의 코드변환(ASCII, EBCDIC, binary등), 파일변환, 문장 축소화 등&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가상 터미널 규약&lt;/b&gt; : 라인과 스크린 길이, 라인의 종료 약속, 페이지 모드, 커서의 특성 등 상호간 다름. -&amp;gt; 가상 터미널 만듦.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;7계층(L7): 응용계층(Application Layer)&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자(사람 또는 소프트웨어)가 네트워크에 접근할 수 있도록 &lt;b&gt;사용자 인터페이스&lt;/b&gt; 제공.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 단위 :&lt;b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;data&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전자메일, 가상단말, 파일 송수신, 웹 응용&lt;/li&gt;
&lt;li&gt;OSI 프로토콜 : FTAM(파일 접근, 전송 및 관리), CMIP&lt;/li&gt;
&lt;li&gt;TCP/IP프로토콜 : FTP, SMTP, HTTP, TELNET&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>학과공부/데이터통신</category>
      <category>OSI</category>
      <category>개방 시스템 상호 연결</category>
      <author>FoO__511</author>
      <guid isPermaLink="true">https://foo-511.tistory.com/8</guid>
      <comments>https://foo-511.tistory.com/8#entry8comment</comments>
      <pubDate>Tue, 17 Oct 2023 14:04:38 +0900</pubDate>
    </item>
    <item>
      <title>데이터 분석과 결측치 제거(feat. Kaggle)</title>
      <link>https://foo-511.tistory.com/10</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;1학기 시작 후 7주차의 절반쯤 지난 지금, 기계학습 보다는 &lt;b&gt;데이터 분석&lt;/b&gt;부터 공부 중이다. 각종 파이썬 라이브러리로 데이터를 분석, 시각화, 이상치 및 결측치 제거 등을 공부했다. 중간고사가 지나면 &lt;b&gt;scikit-learn&lt;/b&gt;이라는 라이브러리를 통해 기계학습을 본격적으로 배우기 시작할 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 &lt;b&gt;중간고사 대비&lt;/b&gt; 겸으로 결측치 제거를 캐글의 데이터셋을 분석하며 정리하려고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1650126701055&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 위의 라이브러리들을 불러온 상태에서 시작한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;데이터 불러오기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.kaggle.com/datasets/rush4ratio/video-game-sales-with-ratings&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.kaggle.com/datasets/rush4ratio/video-game-sales-with-ratings&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 캐글에서 데이터를 다운로드 받고, 판다스의 read_csv()함수를 이용해 데이터를 불러온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;위 데이터셋은 &quot;&lt;b&gt;Video Game Sales with Ratings&lt;/b&gt;&quot;으로, &lt;b&gt;심의 등급별 비디오 게임 판매량과 게임의 부가적인 정보들&lt;/b&gt;을 제공한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;pandas.read_csv()&lt;/h4&gt;
&lt;pre id=&quot;code_1650126682597&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sales = pd.read_csv(&quot;데이터 경로&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;read_csv()에 인자로 index_col을 지정할 수 있는데, 그 경우엔 &lt;b&gt;해당 인덱스의 열&lt;/b&gt;이 이 &lt;b&gt;데이터 프레임의 인덱스 역할&lt;/b&gt;을 하게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;데이터 분석&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;pandas.DataFrame.head()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;head()함수를 통해 데이터의 &lt;b&gt;첫 몇 행&lt;/b&gt;을 볼 수 있다. 끝 행을 보려면 tail()을 이용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1650126790286&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sales.head(3)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1326&quot; data-origin-height=&quot;304&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rWxx3/btrzw0yQDo2/LcEsWdfkbxAQDSDRxPfA81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rWxx3/btrzw0yQDo2/LcEsWdfkbxAQDSDRxPfA81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rWxx3/btrzw0yQDo2/LcEsWdfkbxAQDSDRxPfA81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrWxx3%2Fbtrzw0yQDo2%2FLcEsWdfkbxAQDSDRxPfA81%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;681&quot; height=&quot;156&quot; data-origin-width=&quot;1326&quot; data-origin-height=&quot;304&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터셋을 좀 더 직관적으로(예쁘게) 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;pandas.DataFrame.info()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;info()함수를 이용하면 각 &lt;b&gt;열의 이름, null이 아닌 데이터의 개수, 데이터 타입&lt;/b&gt;을 볼 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1650126903913&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sales.info()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WPy2r/btrzyYnzjKr/DwaC88VO90i5vIzfXj6EF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WPy2r/btrzyYnzjKr/DwaC88VO90i5vIzfXj6EF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WPy2r/btrzyYnzjKr/DwaC88VO90i5vIzfXj6EF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWPy2r%2FbtrzyYnzjKr%2FDwaC88VO90i5vIzfXj6EF1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;444&quot; height=&quot;423&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;532&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;null이 아닌 데이터 개수에 차이가 있음에서 &lt;b&gt;결측치의 존재&lt;/b&gt;를 유추할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;pandas.DataFrame.count()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;각 열의 데이터 개수&lt;/b&gt;만 알려주는 함수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1650131538943&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sales.count()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;331&quot; data-origin-height=&quot;389&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqPVw3/btrzCL8qAHx/VkFYgpIuMcBClGVfsNcUD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqPVw3/btrzCL8qAHx/VkFYgpIuMcBClGVfsNcUD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqPVw3/btrzCL8qAHx/VkFYgpIuMcBClGVfsNcUD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqPVw3%2FbtrzCL8qAHx%2FVkFYgpIuMcBClGVfsNcUD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;229&quot; height=&quot;269&quot; data-origin-width=&quot;331&quot; data-origin-height=&quot;389&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;pandas.DataFrame.describe()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;describe()함수를 이용하면 각 열의 &lt;b&gt;개수, 평균, 표준편차, 최소값, 최대값, 25%, 50%, 75%&lt;/b&gt;값을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1650127790397&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sales.describe()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1284&quot; data-origin-height=&quot;348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9gCbL/btrzw2wF6zj/CWZEoBB7GfIkqbVI6RKvxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9gCbL/btrzw2wF6zj/CWZEoBB7GfIkqbVI6RKvxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9gCbL/btrzw2wF6zj/CWZEoBB7GfIkqbVI6RKvxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9gCbL%2Fbtrzw2wF6zj%2FCWZEoBB7GfIkqbVI6RKvxK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1284&quot; height=&quot;348&quot; data-origin-width=&quot;1284&quot; data-origin-height=&quot;348&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;pandas.DataFrame.value_count()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 열의 &lt;b&gt;데이터 종류&lt;/b&gt;와 해당 종류의 &lt;b&gt;데이터가 몇 개&lt;/b&gt; 있는지 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력값이 지저분해져 한 열에 대해서 적용한 예를 든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1650129359183&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sales['Rating'].value_counts()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/epP4gH/btrzxyXeWqR/6FG6KHj5iXqSFatQWtU4TK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/epP4gH/btrzxyXeWqR/6FG6KHj5iXqSFatQWtU4TK/img.png&quot; data-origin-width=&quot;375&quot; data-origin-height=&quot;240&quot; data-is-animation=&quot;false&quot; width=&quot;288&quot; height=&quot;184&quot; data-widthpercent=&quot;42.67&quot; style=&quot;width: 42.171%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/epP4gH/btrzxyXeWqR/6FG6KHj5iXqSFatQWtU4TK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FepP4gH%2FbtrzxyXeWqR%2F6FG6KHj5iXqSFatQWtU4TK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;375&quot; height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAMBw5/btrzzqc5BzN/GLf3TOmXkRHNi28ICV4Sc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAMBw5/btrzzqc5BzN/GLf3TOmXkRHNi28ICV4Sc1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;485&quot; data-origin-height=&quot;231&quot; width=&quot;349&quot; height=&quot;166&quot; style=&quot;width: 56.6662%;&quot; data-widthpercent=&quot;57.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAMBw5/btrzzqc5BzN/GLf3TOmXkRHNi28ICV4Sc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAMBw5%2Fbtrzzqc5BzN%2FGLf3TOmXkRHNi28ICV4Sc1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;485&quot; height=&quot;231&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 값은 &lt;b&gt;심의 등급&lt;/b&gt;이며, 다음 &lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%98%A4%EB%9D%BD_%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4_%EB%93%B1%EA%B8%89_%EC%9C%84%EC%9B%90%ED%9A%8C&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크&lt;/a&gt;에 더 자세한 설명이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;pandas.DataFrame.corr()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;correlation&lt;/b&gt;의 줄임말이며, 각 열들이 서로 어떤 &lt;b&gt;선형 상관관계&lt;/b&gt;에 있는지 계산한 배열을 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1650133314272&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sales.corr()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1240&quot; data-origin-height=&quot;418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4vhoZ/btrzzqKXPBf/kzbbHFZr0n0U5vK9cd9D2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4vhoZ/btrzzqKXPBf/kzbbHFZr0n0U5vK9cd9D2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4vhoZ/btrzzqKXPBf/kzbbHFZr0n0U5vK9cd9D2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4vhoZ%2FbtrzzqKXPBf%2FkzbbHFZr0n0U5vK9cd9D2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;786&quot; height=&quot;265&quot; data-origin-width=&quot;1240&quot; data-origin-height=&quot;418&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 행렬은 각 column끼리의 상관을 계산한 것이기 때문에 &lt;b&gt;대칭행렬&lt;/b&gt;이며 전치하여도 완전히 같은 행렬이 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1이나 -1&lt;/b&gt;에 가까울수록 &lt;b&gt;선형적인 상관관계&lt;/b&gt;가 크며, &lt;b&gt;0에 가까운건&lt;/b&gt; 두 데이터가 상관이 없다는 뜻이 아니라 &lt;b&gt;상관을 선형으로 표현하기 힘들다는 의미&lt;/b&gt;이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상관계수가 &lt;b&gt;양수일 때 양의 상관관계&lt;/b&gt;를 가진다고 하며, &lt;b&gt;음수일 때는 음의 상관관계&lt;/b&gt;를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;pandas.DataFrame.hist()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hist()함수를 이용해 숫자인 값을 히스토그램으로 표현할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1650128140495&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sales.hist(bins=50, figsize=(20, 15))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1aIF4/btrzz50OQDq/qkB39bPKCWyN4SKveqZkkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1aIF4/btrzz50OQDq/qkB39bPKCWyN4SKveqZkkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1aIF4/btrzz50OQDq/qkB39bPKCWyN4SKveqZkkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1aIF4%2Fbtrzz50OQDq%2FqkB39bPKCWyN4SKveqZkkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;627&quot; height=&quot;460&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;568&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;bins&lt;/b&gt;는 &lt;b&gt;막대의 개수&lt;/b&gt;이며, &lt;b&gt;figsize&lt;/b&gt;는 &lt;b&gt;한 히스토그램의 크기&lt;/b&gt;를 결정한다. 막대의 &lt;b&gt;가로는 데이터의 구간&lt;/b&gt;이며 &lt;b&gt;세로는 해당 구간에 몇 개의 데이터&lt;/b&gt;가 있느냐이다. 히스토그램을 통해 각 값들의 &lt;b&gt;분포&lt;/b&gt;를 쉽게 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;seaborn.displot()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;seaborn라이브러리의 displot함수를 통해 &lt;b&gt;가우시안 커널밀도 추정함수&lt;/b&gt;를 함께 그릴 수 있다.(kde라고 하는데, 아직 뭔지 안배웠다.) 책에서는 distplot으로 나와있지만 지금은 &lt;b&gt;displot&lt;/b&gt;으로 쓰기를 권장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1650128913280&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sns.displot(sales['User_Score'], kde=True, bins=50)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;511&quot; data-origin-height=&quot;529&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTwZ3i/btrzxxqupVm/QLYIHPX4mkfRsbjpRgii50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTwZ3i/btrzxxqupVm/QLYIHPX4mkfRsbjpRgii50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTwZ3i/btrzxxqupVm/QLYIHPX4mkfRsbjpRgii50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTwZ3i%2FbtrzxxqupVm%2FQLYIHPX4mkfRsbjpRgii50%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;343&quot; height=&quot;355&quot; data-origin-width=&quot;511&quot; data-origin-height=&quot;529&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;pandas.plotting.scatter_matrix()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 열을 두 개씩 모든 경우의 수로 조합해 &lt;b&gt;scatter_matrix()&lt;/b&gt;를 그려볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1650132513562&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pd.plotting.scatter_matrix(sales, figsize=(12, 8))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;761&quot; data-origin-height=&quot;542&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9OVOx/btrzxyJIl0N/7c56rrIj1QN67FrFnDUE20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9OVOx/btrzxyJIl0N/7c56rrIj1QN67FrFnDUE20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9OVOx/btrzxyJIl0N/7c56rrIj1QN67FrFnDUE20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9OVOx%2FbtrzxyJIl0N%2F7c56rrIj1QN67FrFnDUE20%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;761&quot; height=&quot;542&quot; data-origin-width=&quot;761&quot; data-origin-height=&quot;542&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 값들이 서로 &lt;b&gt;어떤 관계&lt;/b&gt;를 보이는지 바로 확인할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1차 함수 모양&lt;/b&gt;을 이루는 것들은 &lt;b&gt;선형 관계&lt;/b&gt;를 가진다고 볼 수 있으며, &lt;b&gt;피어슨 상관계수&lt;/b&gt;(corr()으로 구한것)도 높게 나타난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;seaborn.heatmap()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 구한 &lt;b&gt;상관계수 행렬&lt;/b&gt;을 시본 라이브러리의 히트맵으로 표현하면 &lt;b&gt;각 열의 상관관계를 한눈에&lt;/b&gt; 파악할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1650133572852&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;corr_matrix = sales.corr() 
plt.figure(figsize=(15,15))
sns.heatmap(corr_matrix, annot=True, fmt = '.2f', linewidths=.5, cmap='Blues')&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;783&quot; data-origin-height=&quot;799&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7llD1/btrzv5svrSo/ztpZlmJd2kRf6CBhjaUYS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7llD1/btrzv5svrSo/ztpZlmJd2kRf6CBhjaUYS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7llD1/btrzv5svrSo/ztpZlmJd2kRf6CBhjaUYS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7llD1%2Fbtrzv5svrSo%2FztpZlmJd2kRf6CBhjaUYS1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;611&quot; height=&quot;623&quot; data-origin-width=&quot;783&quot; data-origin-height=&quot;799&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;annot=True&lt;/b&gt;옵션으로 &lt;b&gt;각 셀의 수치&lt;/b&gt;를 표시(이 경우엔 각 열끼리의 상관계수)할 수 있다. &lt;b&gt;fmt &lt;/b&gt;옵션으로 숫자를 포맷팅한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;pandas.DataFrame.plot()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;plot()함수로 &lt;b&gt;간단히 시각화&lt;/b&gt;를 할 수 있다. kind=&quot;scatter&quot;옵션을 줬는데, pandas.DataFrame.plot.scatter()과 같은 뜻이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;742&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DPyqV/btrzvUe5JWl/1ubnfj4fIG7KhkG15roFA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DPyqV/btrzvUe5JWl/1ubnfj4fIG7KhkG15roFA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DPyqV/btrzvUe5JWl/1ubnfj4fIG7KhkG15roFA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDPyqV%2FbtrzvUe5JWl%2F1ubnfj4fIG7KhkG15roFA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;775&quot; height=&quot;670&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;742&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵션 중 c는 color, cmap은 color map을 뜻한다. s는 size이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결측치 찾기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결측치를 찾기 위해 isna()나 isnull()함수를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1650127315529&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sales.isna()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;675&quot; data-origin-height=&quot;522&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdxypz/btrzBIYoo4n/0KpKz67Cs5Fekv4QIubyak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdxypz/btrzBIYoo4n/0KpKz67Cs5Fekv4QIubyak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdxypz/btrzBIYoo4n/0KpKz67Cs5Fekv4QIubyak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbdxypz%2FbtrzBIYoo4n%2F0KpKz67Cs5Fekv4QIubyak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;579&quot; height=&quot;448&quot; data-origin-width=&quot;675&quot; data-origin-height=&quot;522&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 &lt;b&gt;boolean 배열&lt;/b&gt;을 반환하는데, 원래 데이터프레임에서 &lt;b&gt;해당 칸이 null이나 NaN&lt;/b&gt;이면 이 boolean배열에서 &lt;b&gt;그 칸은 True&lt;/b&gt;가 된다. &lt;b&gt;유효한 값이 있으면 그 칸은 False&lt;/b&gt;가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;isna()나 isnull()으로 얻은 boolean 배열에&lt;b&gt; sum()함수&lt;/b&gt;를 이용해 좀 더 보기 편하게 바꿀 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1650127451221&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sales.isna().sum()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;342&quot; data-origin-height=&quot;393&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ca3REt/btrzyXCdYs2/UtdxGsp1KSM1mvwnKSQCc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ca3REt/btrzyXCdYs2/UtdxGsp1KSM1mvwnKSQCc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ca3REt/btrzyXCdYs2/UtdxGsp1KSM1mvwnKSQCc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fca3REt%2FbtrzyXCdYs2%2FUtdxGsp1KSM1mvwnKSQCc0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;278&quot; height=&quot;319&quot; data-origin-width=&quot;342&quot; data-origin-height=&quot;393&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제&lt;b&gt; 각 행별로 결측치가 몇 개 있는지&lt;/b&gt; 한 눈에 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 행의 결측치를 알고 싶을 때는 해당 행을 인덱스로 지정하고 위와 똑같이 isna()함수를 사용하면 된다. 그러면 boolean 1차 배열을 반환한다.&lt;/p&gt;
&lt;pre id=&quot;code_1650127903429&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sales['Year_of_Release'].isna()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;457&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/No8zd/btrzx9bOtzv/is2kUJo5PEksbvTqnd8msK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/No8zd/btrzx9bOtzv/is2kUJo5PEksbvTqnd8msK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/No8zd/btrzx9bOtzv/is2kUJo5PEksbvTqnd8msK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNo8zd%2Fbtrzx9bOtzv%2Fis2kUJo5PEksbvTqnd8msK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;309&quot; height=&quot;207&quot; data-origin-width=&quot;457&quot; data-origin-height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 위의 배열을 &lt;b&gt;데이터 프레임의 인덱스&lt;/b&gt;로 넣으면, 그 값이 &lt;b&gt;True인 인덱스의 열만 추출&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1650127985470&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sales[sales['Year_of_Release'].isna()]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;477&quot; data-origin-height=&quot;561&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beYApZ/btrzyWXDtb5/9w80VQJZR4juThss7f1lGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beYApZ/btrzyWXDtb5/9w80VQJZR4juThss7f1lGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beYApZ/btrzyWXDtb5/9w80VQJZR4juThss7f1lGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeYApZ%2FbtrzyWXDtb5%2F9w80VQJZR4juThss7f1lGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;343&quot; height=&quot;403&quot; data-origin-width=&quot;477&quot; data-origin-height=&quot;561&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결측치 제거&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결측치는 크게 두 가지 방법으로 처리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. &lt;b&gt;결측치&lt;/b&gt;가 있는 &lt;b&gt;행이나 열&lt;/b&gt;을 &lt;b&gt;제거&lt;/b&gt;하기.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. &lt;b&gt;특정 값&lt;/b&gt;으로 &lt;b&gt;결측치&lt;/b&gt;를 모두 &lt;b&gt;채우기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결측치가 있는 행이나 열을 제거&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결측치가 있는 열을 제거한다는 것은 데이터 손실을 불러와 학습에 영향을 줄 수 있음에 유의하여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 데이터는 위에서 구한 것처럼 &lt;i&gt;출시년도, 제작사, 비판 점수, 비판 개수, 유저 점수, 유저 개수, 등급&lt;/i&gt;에 결측치가 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;366&quot; data-origin-height=&quot;392&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCi0Ox/btrzxybQAz5/pZ3H9smW9vCFkdutGhzNV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCi0Ox/btrzxybQAz5/pZ3H9smW9vCFkdutGhzNV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCi0Ox/btrzxybQAz5/pZ3H9smW9vCFkdutGhzNV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCi0Ox%2FbtrzxybQAz5%2FpZ3H9smW9vCFkdutGhzNV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;257&quot; height=&quot;275&quot; data-origin-width=&quot;366&quot; data-origin-height=&quot;392&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 중에서 특정 값으로 대체하기 애매한 &lt;b&gt;출시년도&lt;/b&gt;와 &lt;b&gt;제작사&lt;/b&gt;가 있는 행을 제거해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;pandas.DataFrame.dropna()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dropna()함수를 이용하면 결측치가 있는 행을 모두 없앨 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1650130166905&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;test = sales.dropna()
test.isna().sum()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;331&quot; data-origin-height=&quot;410&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3tmjm/btrzz6rUr5e/crbFvskqnWCjPddGyny790/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3tmjm/btrzz6rUr5e/crbFvskqnWCjPddGyny790/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3tmjm/btrzz6rUr5e/crbFvskqnWCjPddGyny790/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3tmjm%2Fbtrzz6rUr5e%2FcrbFvskqnWCjPddGyny790%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;259&quot; height=&quot;321&quot; data-origin-width=&quot;331&quot; data-origin-height=&quot;410&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보다시피 결측치가 싹 사라졌다. 하지만 우리가 원하는 것은 출시년도와 제작사의 결측치를 없애는 것뿐이니 dropna()의 &lt;b&gt;subset 옵션&lt;/b&gt;을 사용하도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1650130321759&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sales = sales.dropna(subset=['Year_of_Release', 'Publisher'])
sales.isna().sum()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;662&quot; data-origin-height=&quot;407&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGdO0M/btrzxavxmME/eY8FVmhlzceuKfOyFoKJR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGdO0M/btrzxavxmME/eY8FVmhlzceuKfOyFoKJR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGdO0M/btrzxavxmME/eY8FVmhlzceuKfOyFoKJR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGdO0M%2FbtrzxavxmME%2FeY8FVmhlzceuKfOyFoKJR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;491&quot; height=&quot;302&quot; data-origin-width=&quot;662&quot; data-origin-height=&quot;407&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 isna().sum()끝에 인덱싱을 통해 원하는 결과만 볼 수 있다. (sales뒤에 넣든 isna()뒤에 넣든 sum()뒤에 넣든 상관없다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;487&quot; data-origin-height=&quot;106&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckXBXp/btrzzqxoTVh/XU8JjkLFkYJqJK41ku4KRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckXBXp/btrzzqxoTVh/XU8JjkLFkYJqJK41ku4KRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckXBXp/btrzzqxoTVh/XU8JjkLFkYJqJK41ku4KRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckXBXp%2FbtrzzqxoTVh%2FXU8JjkLFkYJqJK41ku4KRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;487&quot; height=&quot;106&quot; data-origin-width=&quot;487&quot; data-origin-height=&quot;106&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;특정 값으로 결측치를 모두 채우기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결측치가 있는 행을 없애지 않고 해당 칸을 다른 값으로 보충하는 경우에는 보충할 값을 잘 선택해 원하는 학습 결과를 훼손하지 않도록 주의해야 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;372&quot; data-origin-height=&quot;387&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cVBjd9/btrzAWbwSxR/9YyevAAOjqDOpJib2xBweK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cVBjd9/btrzAWbwSxR/9YyevAAOjqDOpJib2xBweK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cVBjd9/btrzAWbwSxR/9YyevAAOjqDOpJib2xBweK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcVBjd9%2FbtrzAWbwSxR%2F9YyevAAOjqDOpJib2xBweK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;256&quot; height=&quot;266&quot; data-origin-width=&quot;372&quot; data-origin-height=&quot;387&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;남아 있는 값들 중에 Rating은 RP(&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;Rating Pending 등급 미정)으로 채우고 나머지에는 평균값을 넣어보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;pandas.DataFrame.fillna()&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202122;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;fillna()함수의 &lt;b&gt;첫번째 인자&lt;/b&gt;로 특정 값을 넣으면 &lt;b&gt;그 값으로 NaN값이 채워진다&lt;/b&gt;. 이때 &lt;b&gt;inplace=True&lt;/b&gt;옵션을 주면 &lt;b&gt;원본 배열&lt;/b&gt;이 결측치가 채워진 배열로 &lt;b&gt;덮어써지게 된다&lt;/b&gt;. 옵션을 주지 않으면 원본 배열은 변하지 않는다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1650130776047&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sales['Rating'].fillna(&quot;RP&quot;, inplace=True)
sales[&quot;Rating&quot;].isna().sum()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;502&quot; data-origin-height=&quot;97&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9JWPp/btrzCMfbPJ8/RCYaPKOQU0P3SSJPNm6qfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9JWPp/btrzCMfbPJ8/RCYaPKOQU0P3SSJPNm6qfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9JWPp/btrzCMfbPJ8/RCYaPKOQU0P3SSJPNm6qfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9JWPp%2FbtrzCMfbPJ8%2FRCYaPKOQU0P3SSJPNm6qfk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;471&quot; height=&quot;91&quot; data-origin-width=&quot;502&quot; data-origin-height=&quot;97&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 &lt;b&gt;Rating열의 결측치는 모두 &quot;RP&quot;로&lt;/b&gt; 채워졌다. 만약 데이터프레임에 인덱스없이 바로 fillna()함수를 적용한다면 모든 결측치가 &quot;RP&quot;로 채워졌을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결측치가 있는 나머지 행들에는 &lt;b&gt;평균값&lt;/b&gt;을 넣어보자. &lt;b&gt;fillna()&lt;/b&gt;의 &lt;b&gt;첫번째 인자&lt;/b&gt;에 &lt;b&gt;데이터프레임의 평균값 배열&lt;/b&gt;을 넣으면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1650131264035&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;result = sales.fillna(sales.mean(numeric_only=True))
result.isna().sum()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mean()함수의 &lt;b&gt;numeric_only=True&lt;/b&gt; 옵션은 해당 데이터프레임에서 &lt;b&gt;숫자값을 가지는 칼럼에 대해서만 평균값&lt;/b&gt;을 &lt;b&gt;계산&lt;/b&gt;하겠다는 뜻이다. 옵션을 주지 않으면 아래 경고를 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;FutureWarning: Dropping of nuisance columns in DataFrame reductions (with 'numeric_only=None') is deprecated; in a future version this will raise TypeError. Select only valid columns before calling the reduction.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 &lt;b&gt;결측치 제거&lt;/b&gt;가 끝났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;507&quot; data-origin-height=&quot;418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXiq8H/btrzBJ36o7V/9XfIoX6vjiRTqqGzsEqa4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXiq8H/btrzBJ36o7V/9XfIoX6vjiRTqqGzsEqa4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXiq8H/btrzBJ36o7V/9XfIoX6vjiRTqqGzsEqa4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXiq8H%2FbtrzBJ36o7V%2F9XfIoX6vjiRTqqGzsEqa4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;384&quot; height=&quot;316&quot; data-origin-width=&quot;507&quot; data-origin-height=&quot;418&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에선 &lt;b&gt;이산치 제거&lt;/b&gt;를&amp;nbsp;다룰 생각이다.&lt;/p&gt;</description>
      <category>학과공부/기계학습</category>
      <category>kaggle</category>
      <category>matplotlib</category>
      <category>pandas</category>
      <category>seaborn</category>
      <category>결손치 제거</category>
      <category>결측치 제거</category>
      <category>데이터 분석</category>
      <author>FoO__511</author>
      <guid isPermaLink="true">https://foo-511.tistory.com/10</guid>
      <comments>https://foo-511.tistory.com/10#entry10comment</comments>
      <pubDate>Tue, 17 Oct 2023 14:04:11 +0900</pubDate>
    </item>
    <item>
      <title>다형성: Polymorphism</title>
      <link>https://foo-511.tistory.com/6</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;다형성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다형성이라는 말은 이번에 처음 들어봤지만 생각보다 이해하기 쉬웠다. 짧게 말하면 어떤 객체를 다른 객체로 선언할 수 있다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Student bonbon = new Student();

Person bonbon = new Student();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위처럼 객체를 &lt;code&gt;Person&lt;/code&gt; 타입으로 선언했지만 &lt;code&gt;Student&lt;/code&gt;로 초기화할 수 있다. 대박~ 그런데 다 되는 것은 아니다. ㄱ-&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위키백과의 다형성 정의는 이렇다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램 언어의 다형성(多形性, polymorphism; 폴리모피즘)은 그 프로그래밍 언어의 자료형 체계의 성질을 나타내는 것으로, &lt;b&gt;프로그램 언어의 각 요소들(상수, 변수, 식, 오브젝트, 함수, 메소드 등)이 다양한 자료형(type)에 속하는 것이 허가되는 성질&lt;/b&gt;을 가리킨다. 반댓말은 단형성(monomorphism)으로, 프로그램 언어의 각 요소가 한가지 형태만 가지는 성질을 가리킨다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요컨대 각 요소가 다양한 객체가 될 수 있다는 것이다. 직접 다형성을 띠는 객체를 만들어보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;다형성을 띠는 객체&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;선언과 초기화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 말했듯 아무 객체로 선언해서 아무 객체로 초기화할 수 있는건 아니다. &lt;b&gt;부모클래스로 선언&lt;/b&gt;한 변수에 &lt;b&gt;자식클래스로 초기화&lt;/b&gt;가 가능하다. 그 반대의 경우는 불가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;[부모클래스] [변수명] = new [자식클래스]();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀 더 구체적으로 예를 들자면:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class Animal{}
class Dog extends Animal{}

public class Blog {
    public static void main(String[] args){

        Animal littleDog = new Dog();

    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 이 객체는 &lt;b&gt;자식클래스에서 추가된 클래스와 메서드에 접근&lt;/b&gt;할 수 있을까? 자식클래스에서 &lt;b&gt;메서드를 &lt;code&gt;override&lt;/code&gt;한 경우&lt;/b&gt;에는 부모클래서의 메서드를 사용할까 자식클래스의 메서드를 사용할까?&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;필드와 메서드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트를 해보기 위해 먼저 부모 클래스에서 필드 1개와 메서드를 2개 만들어보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;class Animal{
    String name=&quot;littleAnimal&quot;;

    Animal(){}
    Animal(String name){
        this.name = name;
    }

    void eat(){System.out.println(&quot;Animal is eating..&quot;);}
    void drink(){System.out.println(&quot;Animal is drinking..&quot;);}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자식클래스에서는 새로운 필드와 메서드를 하나씩 추가하고 부모클래스의 메서드 중 하나를 &lt;code&gt;override&lt;/code&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class Dog extends Animal{
    int age = 10;

    Dog(){}
    Dog(String name, int age){
        super(name);
        this.age = age;
    }

    void drink(){System.out.println(&quot;Dog is drinking..&quot;);}
    void sniff(){System.out.println(&quot;Dog is sniffing..&quot;);}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 메인 메서드에서 새 객체의 필드와 메서드에 접근해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Blog {
    public static void main(String[] args){
        Animal littleDog = new Dog();

        // 필드에 접근
        System.out.println(littleDog.name);
        System.out.println(littleDog.age);

        // 메서드에 접근
        littleDog.eat();
        littleDog.drink();
        littleDog.sniff();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드를 실행하면 에러가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Blog.java:32: error: cannot find symbol
        System.out.println(littleDog.age);
                                    ^
  symbol:   variable age
  location: variable littleDog of type Animal
Blog.java:37: error: cannot find symbol
        littleDog.sniff();
                 ^
  symbol:   method sniff()
  location: variable littleDog of type Animal
2 errors&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부모객체에 없는 필드와 메서드인 &lt;code&gt;age&lt;/code&gt;, &lt;code&gt;sniff()&lt;/code&gt;를 찾을 수 없다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;type&lt;/code&gt;이 &lt;code&gt;Animal&lt;/code&gt;이 되면서 &lt;b&gt;부모인 &lt;code&gt;Animal&lt;/code&gt;에 없는 필드와 메서드에 접근할 수 없어&lt;/b&gt;지는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류가 나는 부분을 주석처리하고 실행했을 때의 결과는 이렇다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;littleAnimal
Animal is eating..
Dog is drinking..&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자식클래스에서 &lt;b&gt;&lt;code&gt;override&lt;/code&gt;한 메서드가 실행&lt;/b&gt;된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;위의 의문에 대한 해답&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그렇다면 이 객체는 &lt;b&gt;자식클래스에서 추가된 클래스와 메서드에 접근&lt;/b&gt;할 수 있을까?&lt;br /&gt;&lt;b&gt;-&amp;gt; 접근 불가&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;자식클래스에서 &lt;b&gt;메서드를 &lt;code&gt;override&lt;/code&gt;한 경우&lt;/b&gt;에는 부모클래서의 메서드를 사용할까 자식클래스의 메서드를 사용할까?&lt;br /&gt;&lt;b&gt;-&amp;gt; &lt;code&gt;override&lt;/code&gt;한 메서드 사용&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자식클래스 고유 필드와 메서드에 접근하려면&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 &lt;code&gt;Dog&lt;/code&gt;클래스라는 객체를 만들고 &lt;code&gt;littleDog&lt;/code&gt;라는 변수에는 그 &lt;b&gt;주소만&lt;/b&gt; 저장된다. 그러면 &lt;code&gt;Dog&lt;/code&gt;클래스에만 있는 필드와 메서드도 &lt;b&gt;메모리 어딘가에 있어야&lt;/b&gt; 하는것 아닌가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 그 필드와 메서드를 사용하려면 어떡해야할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수의 타입을 바꿔주면(캐스트) 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Blog {
    public static void main(String[] args){
        Animal littleDog = new Dog(&quot;bigDog&quot;, 15);

        // 필드에 접근
        System.out.println(littleDog.name);

        // 캐스드
        System.out.println(((Dog)littleDog).age);

        // 메서드에 접근
        littleDog.eat();
        littleDog.drink();

        // 캐스트
        ((Dog)littleDog).sniff();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;bigDog
15
Animal is eating..
Dog is drinking..
Dog is sniffing..&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;다형성이 생기는 원리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 위의 예를 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class Animal{}
class Dog extends Animal{}

public class Blog {
    public static void main(String[] args){

        Animal littleDog = new Dog();

    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예에서 &lt;code&gt;littleDog&lt;/code&gt;의 &lt;b&gt;자료형은 &lt;code&gt;Animal&lt;/code&gt;&lt;/b&gt;이다. 하지만 초기화는 &lt;code&gt;Dog&lt;/code&gt; 클래스로 되어서 &lt;b&gt;속은 &lt;code&gt;Dog&lt;/code&gt;&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;인간 친화적으로 설명하면&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개는 항상 동물이므로 상위분류인 동물이라고 해도 된다.&lt;br /&gt;하지만 동물은 개일수도 있고 고양이일 수도 있으므로 그 하위분류인 개나 고양이라고 섣불리 말할 순 없다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;컴퓨터 친화적으로 설명하자면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(사실 이건 여기저기 찾아보며 도출해낸 추측이다.. 틀렸을 수도 있다ㅋ 정정 환영합니다!! 제발 알려주세요!)&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체를 생성하면 메서드는 코드 영역, 객체 자체는 힙 영역, 선언한 변수는 객체가 있는 힙 영역의 주소로서 스택 영역에 저장된다. 필드는 원시 데이터타입의 경우 코드 영역, 객체일 경우 힙 영역에 저장된다. &lt;br /&gt;&lt;br /&gt;자식객체가 생성되면서 자식객체가 가지고 있는 모든 메서드는 코드 영역에, 모든 필드는 종류에 따라 코드 영역과 힙 영역에 올라간다. 인스턴스 자체는 힙 영역에 올라간다. &lt;br /&gt;&lt;br /&gt;클래스 내부에서 초기화한 기본 필드값은 자식쪽에서 재선언하더라도 부모의 것으로 저장되고, 메서드는 자식측에서 override한 것이 저장된다. 왜인지는 모르겠다. 자식객체만 가지고 있는 필드와 메서드도 당연히 메모리에 올라간다.&lt;br /&gt;&lt;br /&gt;이제 객체 생성이 끝났고, 객체 주소를 변수에 저장하고 그 변수의 타입을 지정한다. 자바는 그 타입에 따라 변수에서 접근할 수 있는 필드와 메서드들을 판단한다. 타입이 다르므로 자식클래스에만 있는 필드와 메서드에는 접근할 수 없지만, 캐스트를 통해 타입을 바꿔주면 접근 가능해진다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;다형성 활용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 이 다형성을 어디 써먹어야 할까?&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;배열&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다형성을 배열에 이용하면 편하다. 파이썬같은 고레벨 언어와는 달리 자바나 C/C++같은 언어들의 &lt;b&gt;배열은 한가지 타입만 사용&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업을 하다가 개 클래스도 만들고 고양이 클래스도 만들고 토끼 클래스도 만들었는데, 배열 하나에 넣어 관리하고 싶어도 그러지 못한다. 하지만 &lt;b&gt;다형성&lt;/b&gt;을 활용하면 &lt;b&gt;다른 타입의 클래스들도 한 배열 안에&lt;/b&gt; 넣을 수 있다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class Animal{}

class Dog extends Animal{}
class Cat extends Animal{}
class Rabit extends Animal{}


public class Blog {
    public static void main(String[] args){
        Animal[] cuteBuddies = new Animal[10];

        cuteBuddies[0] = new Dog();
        cuteBuddies[1] = new Cat();
        cuteBuddies[2] = new Rabit();

        for (Animal animal: cuteBuddies){
            System.out.print(animal + &quot;  &quot;);
        }

    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;Dog@2eafffde  Cat@59690aa4  Rabit@6842775d  null  null  null  null  null  null  null&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;매개변수&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바 프로그래밍을 하며 &lt;b&gt;매개변수의 타입을 메서드 정의와 다르게 넣으면 오류가 발생&lt;/b&gt;한다. 이때 &lt;b&gt;모든 클래스의 부모인 &lt;code&gt;Object&lt;/code&gt;&lt;/b&gt; 를 매개변수의 타입으로 사용하면 아무 값이나 넣어도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html&quot;&gt;Object 클래스 공식문서&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;class Dog{}

public class Blog {
    static void putAnything(Object imAnything){System.out.println(imAnything);}

    public static void main(String[] args){
        putAnything(1);
        putAnything(&quot;가나다&quot;);
        putAnything(new Dog());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결과:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1
가나다
Dog@41a4555e&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;끝내며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JVM을 리버싱할 능력이 안되니 실제로 메모리에서 무슨 일이 일어나는지 확실히는 모르겠으나, 이 정도만으로도 다형성을 활용하기에는 문제가 없을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Object&lt;/code&gt;클래스에 대해 자세히 공부하면 좋을 듯 하다.&lt;/p&gt;</description>
      <category>Programming/Java</category>
      <category>Java</category>
      <category>Polymorphism</category>
      <category>다형성</category>
      <author>FoO__511</author>
      <guid isPermaLink="true">https://foo-511.tistory.com/6</guid>
      <comments>https://foo-511.tistory.com/6#entry6comment</comments>
      <pubDate>Tue, 17 Oct 2023 14:03:08 +0900</pubDate>
    </item>
  </channel>
</rss>