{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1차시 실습 — 위성 이미지를 숫자로 본다\n",
    "\n",
    "**우송고 위성 AI 8차시 / 1차시**  \n",
    "2026-05-27\n",
    "\n",
    "---\n",
    "\n",
    "## 오늘 할 일\n",
    "\n",
    "1. Colab 에서 Python 셀을 실행한다\n",
    "2. 인터넷에서 위성/우주 이미지를 불러온다\n",
    "3. 이미지를 화면에 띄운다\n",
    "4. 이미지의 크기, 픽셀값, RGB 구조를 확인한다\n",
    "5. RGB 채널을 분리해서 본다\n",
    "\n",
    "## 핵심 메시지\n",
    "\n",
    "> **컴퓨터는 사진을 의미가 아니라 숫자로 본다.**  \n",
    "> 위성 AI 도 결국 이 숫자 배열을 읽는 일이다.\n",
    "\n",
    "## 사용법\n",
    "\n",
    "- 각 코드 셀 왼쪽의 ▶ 버튼을 누르면 실행된다.\n",
    "- 또는 셀을 선택하고 `Shift + Enter`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## STEP 1. 라이브러리 준비\n",
    "\n",
    "이미지 처리에 쓸 도구를 불러온다.\n",
    "\n",
    "- `PIL.Image` — 이미지 열기/저장\n",
    "- `matplotlib.pyplot` — 화면에 그리기\n",
    "- `requests` — 인터넷에서 파일 가져오기\n",
    "- `numpy` — 숫자 배열 처리\n",
    "\n",
    "Colab 에는 모두 미리 설치되어 있다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from PIL import Image\n",
    "import matplotlib.pyplot as plt\n",
    "import requests\n",
    "from io import BytesIO\n",
    "import numpy as np\n",
    "\n",
    "print('준비 완료')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## STEP 2. 위성/우주 이미지 불러오기\n",
    "\n",
    "Apollo 17 에서 찍은 지구 사진(블루 마블)을 불러온다.\n",
    "\n",
    "이미지 출처: NASA / Wikimedia Commons (퍼블릭 도메인)\n",
    "\n",
    "**중요** — 학교망/Colab/Wikimedia 상태에 따라 특정 이미지 URL이 막힐 수 있다.  \n",
    "그래서 아래 코드는 여러 URL을 순서대로 시도하고, 그래도 실패하면 실습이 멈추지 않도록 예비 이미지를 자동으로 만든다.\n",
    "\n",
    "다른 이미지를 쓰고 싶으면 `IMAGE_URLS` 의 첫 번째 주소를 바꾸면 된다.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "IMAGE_URLS = [\n",
    "    # Wikimedia thumb 주소는 가끔 크기별 URL이 막힐 수 있어서 Special:FilePath를 먼저 쓴다.\n",
    "    'https://commons.wikimedia.org/wiki/Special:FilePath/The_Earth_seen_from_Apollo_17.jpg?width=1280',\n",
    "    # 백업: 원본 파일\n",
    "    'https://upload.wikimedia.org/wikipedia/commons/9/97/The_Earth_seen_from_Apollo_17.jpg',\n",
    "]\n",
    "\n",
    "\n",
    "def make_fallback_image(size=512):\n",
    "    \"\"\"인터넷/학교망 문제로 다운로드가 실패해도 실습이 멈추지 않게 만드는 예비 이미지.\"\"\"\n",
    "    y, x = np.ogrid[-1:1:size*1j, -1:1:size*1j]\n",
    "    r = np.sqrt(x*x + y*y)\n",
    "\n",
    "    space = np.zeros((size, size, 3), dtype=np.uint8)\n",
    "    space[..., 2] = 28\n",
    "\n",
    "    earth = r <= 0.72\n",
    "    img_arr = space.copy()\n",
    "    img_arr[earth, 0] = (30 + 40 * (1 - r[earth])).astype(np.uint8)\n",
    "    img_arr[earth, 1] = (90 + 110 * (1 - r[earth])).astype(np.uint8)\n",
    "    img_arr[earth, 2] = (150 + 90 * (1 - r[earth])).astype(np.uint8)\n",
    "\n",
    "    # 간단한 구름/대륙 느낌\n",
    "    clouds = earth & (np.sin(18*x + 8*y) > 0.72)\n",
    "    land = earth & (np.sin(7*x - 4*y) + np.cos(8*y) > 1.0)\n",
    "    img_arr[land] = [70, 150, 80]\n",
    "    img_arr[clouds] = [235, 240, 245]\n",
    "\n",
    "    return Image.fromarray(img_arr, 'RGB')\n",
    "\n",
    "\n",
    "def load_image(urls):\n",
    "    headers = {'User-Agent': 'woosong-space-ai-class/1.0'}\n",
    "    errors = []\n",
    "\n",
    "    for url in urls:\n",
    "        try:\n",
    "            response = requests.get(url, timeout=20, headers=headers)\n",
    "            response.raise_for_status()\n",
    "\n",
    "            content_type = response.headers.get('content-type', '')\n",
    "            if 'image' not in content_type:\n",
    "                raise ValueError(f'이미지 응답이 아님: {content_type}')\n",
    "\n",
    "            image = Image.open(BytesIO(response.content)).convert('RGB')\n",
    "            return image, url, len(response.content), errors\n",
    "        except Exception as e:\n",
    "            errors.append(f'{url} -> {type(e).__name__}: {e}')\n",
    "\n",
    "    # 모든 다운로드가 실패하면 예비 이미지로 계속 진행\n",
    "    return make_fallback_image(), 'fallback: generated earth-like image', 0, errors\n",
    "\n",
    "\n",
    "img, used_url, byte_count, errors = load_image(IMAGE_URLS)\n",
    "\n",
    "print('불러오기 성공')\n",
    "print('사용한 이미지:', used_url)\n",
    "print('파일 크기:', byte_count, 'bytes')\n",
    "\n",
    "if errors:\n",
    "    print('참고: 실패한 URL')\n",
    "    for err in errors:\n",
    "        print('-', err)\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## STEP 3. 이미지 화면에 띄우기\n",
    "\n",
    "`matplotlib` 으로 표시한다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure(figsize=(8, 8))\n",
    "plt.imshow(img)\n",
    "plt.axis('off')\n",
    "plt.title('Earth from Apollo 17 (NASA)')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## STEP 4. 이미지 크기와 모드 확인\n",
    "\n",
    "이미지는 가로 × 세로 픽셀로 이루어져 있다.\n",
    "\n",
    "- `img.size` → (가로, 세로)\n",
    "- `img.mode` → 색상 구조. `RGB` 는 빨강/초록/파랑 3채널."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('이미지 크기:', img.size)\n",
    "print('이미지 모드:', img.mode)\n",
    "\n",
    "width, height = img.size\n",
    "print(f'총 픽셀 수: {width * height:,} 개')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## STEP 5. 한 픽셀의 값 보기\n",
    "\n",
    "어떤 위치의 픽셀이든 RGB 세 숫자로 표현된다.\n",
    "\n",
    "- (0, 0, 0) → 검정\n",
    "- (255, 255, 255) → 흰색\n",
    "- (255, 0, 0) → 빨강\n",
    "\n",
    "각 채널은 0 ~ 255 의 정수."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 이미지 중앙에서 한 점 찍어보기\n",
    "x = img.size[0] // 2\n",
    "y = img.size[1] // 2\n",
    "\n",
    "pixel = img.getpixel((x, y))\n",
    "print(f'좌표 ({x}, {y}) 의 픽셀:', pixel)\n",
    "print(f'  빨강 = {pixel[0]}')\n",
    "print(f'  초록 = {pixel[1]}')\n",
    "print(f'  파랑 = {pixel[2]}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**도전 1** — 다른 좌표 (예: (100, 100), (500, 700)) 의 픽셀값도 확인해보자. 검정 우주 부분과 푸른 지구 부분의 값이 어떻게 다른가?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 여기에 직접 코드 작성\n",
    "# 예시:\n",
    "# print(img.getpixel((100, 100)))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## STEP 6. 이미지를 숫자 배열로 변환\n",
    "\n",
    "`numpy` 로 변환하면 이미지가 **3차원 숫자 배열**이 된다.\n",
    "\n",
    "- shape = (세로, 가로, 채널)\n",
    "- 채널 = 3 (R, G, B)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "arr = np.array(img)\n",
    "\n",
    "print('배열 shape:', arr.shape)\n",
    "print('배열 자료형:', arr.dtype)\n",
    "print('최솟값:', arr.min())\n",
    "print('최댓값:', arr.max())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## STEP 7. RGB 채널 분리\n",
    "\n",
    "3차원 배열의 마지막 축을 잘라 R, G, B 따로 본다.\n",
    "\n",
    "- `arr[:, :, 0]` → 빨강만\n",
    "- `arr[:, :, 1]` → 초록만\n",
    "- `arr[:, :, 2]` → 파랑만\n",
    "\n",
    "지구 사진은 푸른색이 강해서 B 채널이 밝게 나올 것이다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "R = arr[:, :, 0]\n",
    "G = arr[:, :, 1]\n",
    "B = arr[:, :, 2]\n",
    "\n",
    "fig, axes = plt.subplots(1, 3, figsize=(15, 5))\n",
    "\n",
    "axes[0].imshow(R, cmap='Reds')\n",
    "axes[0].set_title('Red channel')\n",
    "axes[0].axis('off')\n",
    "\n",
    "axes[1].imshow(G, cmap='Greens')\n",
    "axes[1].set_title('Green channel')\n",
    "axes[1].axis('off')\n",
    "\n",
    "axes[2].imshow(B, cmap='Blues')\n",
    "axes[2].set_title('Blue channel')\n",
    "axes[2].axis('off')\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## STEP 8. 각 채널의 평균값\n",
    "\n",
    "전체 이미지에서 R, G, B 가 평균적으로 얼마인지 본다.  \n",
    "지구 사진이면 B (파랑) 평균이 가장 높을 가능성이 크다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f'R 평균: {R.mean():.1f}')\n",
    "print(f'G 평균: {G.mean():.1f}')\n",
    "print(f'B 평균: {B.mean():.1f}')\n",
    "\n",
    "# 막대 그래프로 비교\n",
    "plt.figure(figsize=(6, 4))\n",
    "plt.bar(['R', 'G', 'B'], [R.mean(), G.mean(), B.mean()],\n",
    "        color=['#FF3B3B', '#3BFF6E', '#3B8BFF'],\n",
    "        edgecolor='black', linewidth=2)\n",
    "plt.title('Average value per channel')\n",
    "plt.ylim(0, 255)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## 도전 과제 (시간 남으면)\n",
    "\n",
    "### 도전 A. 흑백으로 만들기\n",
    "RGB 평균을 내면 흑백 (그레이스케일) 이 된다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "gray = arr.mean(axis=2)\n",
    "\n",
    "plt.figure(figsize=(8, 8))\n",
    "plt.imshow(gray, cmap='gray')\n",
    "plt.axis('off')\n",
    "plt.title('Grayscale')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 도전 B. 다른 위성 이미지로 바꿔보기\n",
    "\n",
    "아래 URL 중 하나를 `IMAGE_URLS` 맨 앞에 넣고 위 코드를 다시 돌려보자.\n",
    "\n",
    "- 야간 지구 (Black Marble Americas): `https://commons.wikimedia.org/wiki/Special:FilePath/Black_Marble_Americas.jpg?width=1280`\n",
    "- 화성: `https://commons.wikimedia.org/wiki/Special:FilePath/OSIRIS_Mars_true_color.jpg?width=960`\n",
    "- 목성: `https://commons.wikimedia.org/wiki/Special:FilePath/Jupiter_and_its_shrunken_Great_Red_Spot.jpg?width=1024`\n",
    "\n",
    "어느 이미지에서 R 평균이 가장 높은가? 왜 그런가?\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 여기에 직접 작성\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 도전 C. 한 채널만 강하게 만들기\n",
    "\n",
    "B (파랑) 값을 모두 0 으로 바꾸면 어떻게 보일까?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "arr_no_blue = arr.copy()\n",
    "arr_no_blue[:, :, 2] = 0   # B 채널 = 0\n",
    "\n",
    "plt.figure(figsize=(8, 8))\n",
    "plt.imshow(arr_no_blue)\n",
    "plt.axis('off')\n",
    "plt.title('Blue removed')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## 추가 미션 D. 진짜 AI처럼 보이는 “행성 판정기” 만들기\n",
    "\n",
    "AI 모델을 새로 학습시키지는 않지만, **이미지의 숫자 특징(feature)** 을 뽑아서 간단한 규칙으로 판정해보자.\n",
    "\n",
    "예를 들어:\n",
    "\n",
    "- 파란색 평균이 높으면 → 바다/지구 느낌\n",
    "- 빨간색 평균이 높으면 → 화성 느낌\n",
    "- 전체가 어두우면 → 야간 지구/우주 느낌\n",
    "\n",
    "이건 완벽한 AI는 아니지만, 머신러닝도 결국 이런 숫자 특징에서 출발한다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def image_features(image):\n",
    "    small = image.resize((256, 256))\n",
    "    data = np.array(small).astype(float)\n",
    "\n",
    "    R = data[:, :, 0]\n",
    "    G = data[:, :, 1]\n",
    "    B = data[:, :, 2]\n",
    "    brightness = data.mean(axis=2)\n",
    "\n",
    "    return {\n",
    "        'R_mean': R.mean(),\n",
    "        'G_mean': G.mean(),\n",
    "        'B_mean': B.mean(),\n",
    "        'brightness': brightness.mean(),\n",
    "        'contrast': brightness.std(),\n",
    "        'dark_ratio': (brightness < 35).mean(),\n",
    "        'blue_score': B.mean() - R.mean(),\n",
    "        'red_score': R.mean() - B.mean(),\n",
    "    }\n",
    "\n",
    "\n",
    "features = image_features(img)\n",
    "\n",
    "for name, value in features.items():\n",
    "    print(f'{name:12s}: {value:.2f}')\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def space_classifier(features):\n",
    "    if features['dark_ratio'] > 0.55:\n",
    "        return 'Night / dark space image'\n",
    "    elif features['red_score'] > 18:\n",
    "        return 'Mars-like red planet'\n",
    "    elif features['blue_score'] > 10 and features['brightness'] > 45:\n",
    "        return 'Earth-like blue planet'\n",
    "    elif features['contrast'] > 55:\n",
    "        return 'Gas giant / strong cloud bands'\n",
    "    else:\n",
    "        return 'Unclear — needs smarter AI'\n",
    "\n",
    "\n",
    "def explain_prediction(label):\n",
    "    explanations = {\n",
    "        'Night / dark space image': '어두운 픽셀 비율이 높아서 야간/우주 이미지처럼 보인다.',\n",
    "        'Mars-like red planet': 'R 평균이 B 평균보다 높아서 붉은 행성처럼 보인다.',\n",
    "        'Earth-like blue planet': 'B 평균이 R 평균보다 높아서 푸른 지구처럼 보인다.',\n",
    "        'Gas giant / strong cloud bands': '밝기 대비가 커서 구름/띠가 강한 행성처럼 보인다.',\n",
    "        'Unclear — needs smarter AI': '단순 규칙만으로는 특징이 애매하다.',\n",
    "    }\n",
    "    return explanations.get(label, '')\n",
    "\n",
    "\n",
    "prediction = space_classifier(features)\n",
    "print('AI 흉내 판정 결과:')\n",
    "print(prediction)\n",
    "print(explain_prediction(prediction))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 여러 우주 이미지를 한 번에 판정해보기\n",
    "\n",
    "아래 코드는 지구/화성/목성/야간 지구 이미지를 불러와서, 우리가 만든 판정기가 어떻게 답하는지 비교한다.\n",
    "\n",
    "완벽하게 맞히는 게 목표가 아니다.  \n",
    "**왜 맞았는지 / 왜 틀렸는지** 설명해보는 게 핵심이다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "TEST_IMAGES = {\n",
    "    'Blue Marble': ['https://commons.wikimedia.org/wiki/Special:FilePath/The_Earth_seen_from_Apollo_17.jpg?width=640'],\n",
    "    'Mars': ['https://commons.wikimedia.org/wiki/Special:FilePath/OSIRIS_Mars_true_color.jpg?width=640'],\n",
    "    'Jupiter': ['https://commons.wikimedia.org/wiki/Special:FilePath/Jupiter_and_its_shrunken_Great_Red_Spot.jpg?width=640'],\n",
    "    'Night Earth': ['https://commons.wikimedia.org/wiki/Special:FilePath/Black_Marble_Americas.jpg?width=640'],\n",
    "}\n",
    "\n",
    "plt.figure(figsize=(12, 9))\n",
    "\n",
    "for i, (name, urls) in enumerate(TEST_IMAGES.items(), start=1):\n",
    "    test_img, used, byte_count, errors = load_image(urls)\n",
    "    f = image_features(test_img)\n",
    "    pred = space_classifier(f)\n",
    "\n",
    "    print(name, '->', pred)\n",
    "    print(' ', explain_prediction(pred))\n",
    "\n",
    "    plt.subplot(2, 2, i)\n",
    "    plt.imshow(test_img)\n",
    "    plt.axis('off')\n",
    "    # Matplotlib 기본 폰트가 한글/이모지를 못 그릴 수 있어서 그래프 제목은 영어만 사용한다.\n",
    "    plt.title(name + '\\n' + pred, fontsize=10)\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## 추가 미션 E. 나만의 “우주 포스터 필터” 만들기\n",
    "\n",
    "이번에는 이미지를 분석하는 데서 끝나지 않고, 숫자 배열을 바꿔서 새 이미지를 만들어보자.\n",
    "\n",
    "아래 필터는 두 가지 일을 한다.\n",
    "\n",
    "1. 색을 몇 단계로 줄여서 포스터처럼 만든다.\n",
    "2. 밝기가 급격히 바뀌는 부분을 검은 선으로 강조한다.\n",
    "\n",
    "`COLOR_LEVELS`, `EDGE_STRENGTH` 값을 바꾸면 결과가 달라진다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "COLOR_LEVELS = 5      # 2~8 사이로 바꿔보기\n",
    "EDGE_STRENGTH = 28    # 10~60 사이로 바꿔보기\n",
    "\n",
    "small = img.resize((512, 512))\n",
    "data = np.array(small).astype(np.float32)\n",
    "\n",
    "# 1) 색 단계 줄이기\n",
    "step = 255 / (COLOR_LEVELS - 1)\n",
    "poster = np.round(data / step) * step\n",
    "poster = np.clip(poster, 0, 255).astype(np.uint8)\n",
    "\n",
    "# 2) 간단한 edge 찾기\n",
    "brightness = data.mean(axis=2)\n",
    "dx = np.abs(brightness - np.roll(brightness, 1, axis=1))\n",
    "dy = np.abs(brightness - np.roll(brightness, 1, axis=0))\n",
    "edges = (dx + dy) > EDGE_STRENGTH\n",
    "\n",
    "poster_with_edges = poster.copy()\n",
    "poster_with_edges[edges] = [0, 0, 0]\n",
    "\n",
    "plt.figure(figsize=(14, 6))\n",
    "\n",
    "plt.subplot(1, 2, 1)\n",
    "plt.imshow(small)\n",
    "plt.axis('off')\n",
    "plt.title('Original')\n",
    "\n",
    "plt.subplot(1, 2, 2)\n",
    "plt.imshow(poster_with_edges)\n",
    "plt.axis('off')\n",
    "plt.title(f'Space Poster Filter: levels={COLOR_LEVELS}, edge={EDGE_STRENGTH}')\n",
    "\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 마지막 미션\n",
    "\n",
    "값을 바꿔가며 가장 마음에 드는 포스터를 만들어보자.\n",
    "\n",
    "추천 실험:\n",
    "\n",
    "- `COLOR_LEVELS = 3`, `EDGE_STRENGTH = 20`\n",
    "- `COLOR_LEVELS = 6`, `EDGE_STRENGTH = 35`\n",
    "- `COLOR_LEVELS = 8`, `EDGE_STRENGTH = 50`\n",
    "\n",
    "마음에 드는 결과가 나오면 스크린샷을 찍고, 제목을 붙여보자.\n",
    "\n",
    "예: **“AI가 본 지구 포스터”**\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## 오늘의 정리\n",
    "\n",
    "체크리스트 — 이 셀들이 다 실행됐는가?\n",
    "\n",
    "- [ ] 이미지를 화면에 띄웠다\n",
    "- [ ] 이미지 크기를 출력했다\n",
    "- [ ] 한 픽셀의 RGB 값을 출력했다\n",
    "- [ ] R, G, B 채널을 분리해서 그렸다\n",
    "- [ ] 채널별 평균을 비교했다\n",
    "- [ ] 간단한 행성 판정기를 만들었다\n",
    "- [ ] 우주 포스터 필터를 만들어봤다\n",
    "\n",
    "## 한 줄 정리\n",
    "\n",
    "> **이미지는 숫자 배열이다.**  \n",
    "> 위성 AI 는 이 숫자에서 패턴을 찾는다.\n",
    "\n",
    "## 다음 차시 예고\n",
    "\n",
    "2차시는 이미지가 아니라 **센서 로그 데이터** (고도, 온도, 배터리, 신호) 를 다룬다.  \n",
    "그래프를 그리고, 이상한 값을 찾는다.\n",
    "\n",
    "## 노트북 저장\n",
    "\n",
    "상단 메뉴 → 파일 → Drive 에 저장.  \n",
    "이름: `01_위성이미지_기초_본인이름.ipynb`\n"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "name": "01_satellite_image_basics.ipynb",
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}