09 July 2026 1 min read5.0 (1)

Tutorial Membuat Foto Kita Blur dengan Python

Haris Riyoni

Author

Tutorial Membuat Foto Kita Blur dengan Python

01 / Project Setup

Project Setup

Tools yang kamu butuhin sebelum mulai, dan perintah untuk install-nya.

Install Steps

6 steps
  1. 01

    Cek versi Python

    • Pastikan Python sudah terinstall dan bisa dijalankan dari terminal. Jika command ini menampilkan versi Python, berarti environment sudah siap digunakan. Jalankan ini untuk mengecek versi:

      $ py --version
  2. 02

    Buat struktur folder

    • Folder Models digunakan untuk menyimpan model program ketika kita menjalankan program di akhir, Buat Folder via Terminal :

      $ mkdir models
    • sebagai pembungkus folder.

      $ mkdir src
    • Folder domain berisi aturan utama.

      $ mkdir src\domain
    • Application berisi logic aplikasi.

      $ mkdir src\application
    • infrastructure berisi integrasi webcam dan MediaPipe.

      $ mkdir src\infrastructure
    • Sedangkan presentation berisi tampilan OpenCV.

      $ mkdir src\presentation
  3. 03

    Buat file __init__.py

    • File __init__.py membuat folder Python dikenali sebagai package. Ini penting agar import antar file di dalam folder src bisa berjalan.

      $ New-Item src\__init__.py
    • $ New-Item src\domain\__init__.py
    • $ New-Item src\application\__init__.py
    • $ New-Item src\infrastructure\__init__.p
    • $ New-Item src\presentation\__init__.py
  4. 04

    Install dependency

    • Sebelum menjalankan Comand ini, kamu harus membuat file nya terlebih dahulu dan isinya ada di bawah di section Code, Command ini menginstall semua library yang dibutuhkan oleh aplikasi, yang tercantum di file requirements.txt dalam project ini yaitu OpenCV, MediaPipe, dan NumPy.

      $ py -m pip install -r requirements.txt
  5. 05

    Cek syntax Python

    • Comand ini dijalankan ketika semua file dan project sudah ada codingannya dan sudah lengkap, fungsinya untuk mengecek apakah ada error syntax pada file Python sebelum aplikasi dijalankan.

      $ py -m compileall .
  6. 06

    Jalankan aplikasi

    • Comand ini untuk menjalakan program aplikasi.

      $ py main.py

02 / Project Structure

Struktur Folder & File

Struktur folder & file yang bakal kamu punya di akhir tutorial.

Explorer20 items
  • project
03

03 / Step

Apa yang akan dibuat?

Kita akan membuat aplikasi webcam sederhana. Saat aplikasi berjalan, kamera laptop akan terbuka. Aplikasi akan membaca tangan user menggunakan MediaPipe. Jika user menunjukkan peace sign atau dua jari, frame kamera akan diberi efek blur.

Selain itu, aplikasi juga menampilkan garis indikator pada jari. Garis ini membantu user melihat bahwa tangan sudah berhasil dideteksi oleh sistem.

04

04 / Step

Membuat file requirements.txt

Langkah pertama adalah membuat file requirements.txt. File ini berisi daftar library yang dibutuhkan project. Dengan file ini, kita cukup menjalankan satu command untuk menginstall semua dependency.

05

05 / Step

Isi requirements.txt

Isi requirements.txt

requirements.txt
opencv-python
mediapipe
numpy
06

06 / Step

Membuat gesture enum

Kita membutuhkan penanda untuk jenis gesture. Pada project ini hanya ada dua kondisi: tidak ada gesture yang cocok, atau gesture peace sign terdeteksi.

07

07 / Step

Kode gesture.py

Kode gesture.py

src/domain/gesture.py
from enum import Enum
class Gesture(str, Enum):
NONE = "none"
PEACE_SIGN = "peace_sign"
08

08 / Step

Menyimpan hasil deteksi tangan

Setelah MediaPipe membaca tangan, kita perlu menyimpan hasilnya. Data yang disimpan adalah gesture yang terdeteksi dan titik landmark tangan. Landmark ini nanti dipakai untuk menggambar garis indikator pada jari.

09

09 / Step

Kode hand_detection.py

Kode hand_detection.py

src/domain/hand_detection.py
from dataclasses import dataclass
from src.domain.gesture import Gesture
@dataclass(frozen=True)
class HandDetection:
gesture: Gesture
landmarks: list[tuple[float, float]]
@property
def has_hand(self) -> bool:
return len(self.landmarks) > 0
10

10 / Step

Membuat interface aplikasi

Interface digunakan agar struktur aplikasi lebih rapi. Kita membuat kontrak untuk kamera dan detector tangan. Dengan cara ini, bagian aplikasi tidak perlu tahu detail cara webcam atau MediaPipe bekerja.

11

11 / Step

Kode interfaces.py

Kode interfaces.py

src/domain/interfaces.py
from abc import ABC, abstractmethod
import numpy as np
from src.domain.hand_detection import HandDetection
class Camera(ABC):
@abstractmethod
def read(self) -> tuple[bool, np.ndarray | None]:
pass
@abstractmethod
def release(self) -> None:
pass
class HandDetector(ABC):
@abstractmethod
def detect(self, frame: np.ndarray) -> HandDetection:
pass
@abstractmethod
def close(self) -> None:
pass
12

12 / Step

Membaca webcam dengan OpenCV

Bagian webcam dibuat di folder infrastructure karena berhubungan langsung dengan perangkat luar, yaitu kamera laptop. OpenCV digunakan untuk membuka kamera dan membaca frame satu per satu.

13

13 / Step

Kode webcam_camera.py

Kode webcam_camera.py

src/infrastructure/webcam_camera.py
import cv2
import numpy as np
from src.domain.interfaces import Camera
class WebcamCamera(Camera):
def __init__(self, camera_index: int = 0) -> None:
self._capture = cv2.VideoCapture(camera_index)
self._capture.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
self._capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
def read(self) -> tuple[bool, np.ndarray | None]:
return self._capture.read()
def release(self) -> None:
self._capture.release()
14

14 / Step

Mendeteksi tangan dengan MediaPipe

MediaPipe digunakan untuk mendeteksi tangan dan membaca 21 titik landmark. Dari titik-titik ini, kita bisa mengetahui posisi ujung jari dan sendi jari. Informasi tersebut dipakai untuk mengenali gesture peace sign.

15

15 / Step

Kode deteksi peace sign

Kode deteksi peace sign

src/infrastructure/mediapipe_hand_detector.py
from pathlib import Path
from urllib.error import URLError
from urllib.request import urlretrieve
import cv2
import mediapipe as mp
import numpy as np
from src.domain.gesture import Gesture
from src.domain.hand_detection import HandDetection
from src.domain.interfaces import HandDetector
class MediaPipeHandDetector(HandDetector):
MODEL_URL = (
"https://storage.googleapis.com/mediapipe-models/hand_landmarker/"
"hand_landmarker/float16/latest/hand_landmarker.task"
)
MODEL_PATH = Path("models") / "hand_landmarker.task"
def __init__(self) -> None:
model_path = self._ensure_model()
base_options = mp.tasks.BaseOptions(model_asset_path=str(model_path))
options = mp.tasks.vision.HandLandmarkerOptions(
base_options=base_options,
running_mode=mp.tasks.vision.RunningMode.VIDEO,
num_hands=1,
min_hand_detection_confidence=0.65,
min_hand_presence_confidence=0.55,
min_tracking_confidence=0.55,
)
self._landmarker = mp.tasks.vision.HandLandmarker.create_from_options(options)
self._timestamp_ms = 0
def detect(self, frame: np.ndarray) -> HandDetection:
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
rgb_frame = np.ascontiguousarray(rgb_frame)
mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=rgb_frame)
self._timestamp_ms += 33
result = self._landmarker.detect_for_video(mp_image, self._timestamp_ms)
if not result.hand_landmarks:
return HandDetection(Gesture.NONE, [])
landmarks = result.hand_landmarks[0]
points = [(landmark.x, landmark.y) for landmark in landmarks]
gesture = Gesture.PEACE_SIGN if self._is_peace_sign(landmarks) else Gesture.NONE
return HandDetection(gesture, points)
def close(self) -> None:
self._landmarker.close()
def _is_peace_sign(self, landmarks: list[object]) -> bool:
index_open = landmarks[8].y < landmarks[6].y
middle_open = landmarks[12].y < landmarks[10].y
ring_closed = landmarks[16].y > landmarks[14].y
pinky_closed = landmarks[20].y > landmarks[18].y
index_middle_gap = abs(landmarks[8].x - landmarks[12].x)
middle_ring_gap = abs(landmarks[12].x - landmarks[16].x)
return (
index_open
and middle_open
and ring_closed
and pinky_closed
and index_middle_gap > 0.035
and middle_ring_gap > 0.025
)
def _ensure_model(self) -> Path:
if self.MODEL_PATH.exists():
return self.MODEL_PATH
self.MODEL_PATH.parent.mkdir(exist_ok=True)
try:
print("Downloading MediaPipe hand model...")
urlretrieve(self.MODEL_URL, self.MODEL_PATH)
except (OSError, URLError) as error:
raise RuntimeError(
"Model MediaPipe belum tersedia. Download hand_landmarker.task "
f"dari {self.MODEL_URL}, lalu simpan ke {self.MODEL_PATH}."
) from error
return self.MODEL_PATH
16

16 / Step

Cara kerja peace sign detection

MediaPipe memberi nomor pada setiap titik jari. Titik 8 adalah ujung telunjuk, titik 12 adalah ujung jari tengah, titik 16 adalah ujung jari manis, dan titik 20 adalah ujung kelingking.

Untuk membaca peace sign, kita mengecek apakah telunjuk dan jari tengah terbuka. Setelah itu, kita mengecek apakah jari manis dan kelingking lebih tertutup. Kita juga mengecek jarak antara telunjuk dan jari tengah agar gesture lebih stabil.

17

17 / Step

Membuat efek blur

Efek blur dibuat di CameraBlurService. Service ini menerima frame dari kamera dan gesture dari detector. Jika gesture adalah PEACE_SIGN, maka blur diaktifkan. Jika gesture hilang, blur dikurangi sampai frame kembali normal.

18

18 / Step

Kode camera_blur_service.py

Kode camera_blur_service.py

src/application/camera_blur_service.py
from dataclasses import dataclass
import cv2
import numpy as np
from src.domain.gesture import Gesture
@dataclass
class CameraBlurState:
blur_strength: float = 0.0
is_blur_active: bool = False
manual_blur: bool = False
class CameraBlurService:
def __init__(self) -> None:
self.state = CameraBlurState()
def toggle_manual_blur(self) -> bool:
self.state.manual_blur = not self.state.manual_blur
return self.state.manual_blur
def process_frame(self, frame: np.ndarray, gesture: Gesture) -> tuple[np.ndarray, CameraBlurState]:
target_active = gesture == Gesture.PEACE_SIGN or self.state.manual_blur
self.state.is_blur_active = target_active
target_blur = 1.0 if target_active else 0.0
easing = 0.12 if target_active else 0.16
self.state.blur_strength += (target_blur - self.state.blur_strength) * easing
if self.state.blur_strength < 0.01:
self.state.blur_strength = 0.0
return self._apply_blur(frame), self.state
def _apply_blur(self, frame: np.ndarray) -> np.ndarray:
if self.state.blur_strength <= 0.0:
return frame.copy()
blur_amount = int(1 + self.state.blur_strength * 70)
kernel_size = blur_amount if blur_amount % 2 == 1 else blur_amount + 1
blurred = cv2.GaussianBlur(frame, (kernel_size, kernel_size), 0)
alpha = min(1.0, self.state.blur_strength)
return cv2.addWeighted(frame, 1.0 - alpha, blurred, alpha, 0)
19

19 / Step

Menampilkan window OpenCV

Bagian presentation bertugas menampilkan kamera ke layar. Di sini aplikasi membaca frame, memanggil detector tangan, memberi efek blur, menggambar indikator jari, lalu menampilkan hasilnya dengan cv2.imshow.

20

20 / Step

Menampilkan garis indikator jari

Agar user tahu tangan sudah terdeteksi, landmark tangan digambar menjadi garis. Saat peace sign aktif, garis pada telunjuk dan jari tengah diberi warna hijau. Ini membuat aplikasi lebih mudah dipahami karena user bisa melihat hasil deteksi secara langsung.

21

21 / Step

Kode indikator jari

Kode indikator jari

src/presentation/opencv_window.py
from __future__ import annotations
import time
from datetime import datetime
from pathlib import Path
import cv2
import numpy as np
from src.application.camera_blur_service import CameraBlurService, CameraBlurState
from src.domain.gesture import Gesture
from src.domain.hand_detection import HandDetection
from src.domain.interfaces import Camera, HandDetector
class OpenCVWindow:
WINDOW_NAME = "Foto Kita Blur"
def __init__(
self,
camera: Camera,
hand_detector: HandDetector,
blur_service: CameraBlurService,
) -> None:
self._camera = camera
self._hand_detector = hand_detector
self._blur_service = blur_service
self._show_helper = True
self._saved_message_until = 0.0
self._latest_frame: np.ndarray | None = None
def run(self) -> None:
cv2.namedWindow(self.WINDOW_NAME, cv2.WINDOW_NORMAL)
while True:
ok, frame = self._camera.read()
if not ok or frame is None:
break
frame = cv2.flip(frame, 1)
detection = self._hand_detector.detect(frame)
rendered_frame, state = self._blur_service.process_frame(frame, detection.gesture)
rendered_frame = self._render_overlay(rendered_frame, state, detection)
self._latest_frame = rendered_frame
cv2.imshow(self.WINDOW_NAME, rendered_frame)
key = cv2.waitKey(1) & 0xFF
if key == ord("q"):
break
if key == ord("s"):
self._save_current_frame()
if key == ord("b"):
self._blur_service.toggle_manual_blur()
if key == ord("h"):
self._show_helper = not self._show_helper
cv2.destroyAllWindows()
def _render_overlay(
self,
frame: np.ndarray,
state: CameraBlurState,
detection: HandDetection,
) -> np.ndarray:
height, width = frame.shape[:2]
overlay = frame.copy()
if detection.has_hand:
self._draw_finger_indicators(overlay, detection.landmarks, detection.gesture)
if self._show_helper:
status = "blur aktif" if state.is_blur_active else "angkat dua jari"
hint = "s save b blur h helper q quit"
self._draw_text(overlay, status, (24, height - 42), scale=0.5, thickness=1)
self._draw_text(overlay, hint, (24, height - 20), scale=0.38, thickness=1, alpha=0.74)
elif detection.gesture != Gesture.PEACE_SIGN and not state.is_blur_active:
self._draw_text(
overlay,
"show peace sign",
(28, height - 28),
scale=0.48,
thickness=1,
alpha=0.72,
)
if time.time() < self._saved_message_until:
text = "saved to captures/"
size, _ = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, 0.48, 1)
self._draw_text(
overlay,
text,
(width - size[0] - 28, height - 26),
scale=0.48,
thickness=1,
alpha=0.9,
)
return overlay
def _draw_finger_indicators(
self,
frame: np.ndarray,
landmarks: list[tuple[float, float]],
gesture: Gesture,
) -> None:
if len(landmarks) < 21:
return
height, width = frame.shape[:2]
points = [(int(x * width), int(y * height)) for x, y in landmarks]
finger_lines = [
(0, 1, 2, 3, 4),
(0, 5, 6, 7, 8),
(0, 9, 10, 11, 12),
(0, 13, 14, 15, 16),
(0, 17, 18, 19, 20),
]
for line in finger_lines:
is_peace_finger = line[1] in (5, 9)
color = (
(80, 255, 120)
if gesture == Gesture.PEACE_SIGN and is_peace_finger
else (230, 230, 230)
)
thickness = 3 if gesture == Gesture.PEACE_SIGN and is_peace_finger else 1
for start_index, end_index in zip(line, line[1:]):
cv2.line(
frame,
points[start_index],
points[end_index],
color,
thickness,
cv2.LINE_AA,
)
for point in points:
cv2.circle(frame, point, 3, (240, 240, 240), -1, cv2.LINE_AA)
if gesture == Gesture.PEACE_SIGN:
cv2.circle(frame, points[8], 5, (80, 255, 120), -1, cv2.LINE_AA)
cv2.circle(frame, points[12], 5, (80, 255, 120), -1, cv2.LINE_AA)
def _draw_text(
self,
frame: np.ndarray,
text: str,
origin: tuple[int, int],
scale: float,
thickness: int,
alpha: float = 0.86,
) -> None:
font = cv2.FONT_HERSHEY_SIMPLEX
shadow_color = (10, 10, 10)
text_color = (238, 238, 238)
shadow = frame.copy()
cv2.putText(
shadow,
text,
(origin[0] + 1, origin[1] + 1),
font,
scale,
shadow_color,
thickness + 2,
cv2.LINE_AA,
)
cv2.addWeighted(shadow, 0.46, frame, 0.54, 0, frame)
text_layer = frame.copy()
cv2.putText(
text_layer,
text,
origin,
font,
scale,
text_color,
thickness,
cv2.LINE_AA,
)
cv2.addWeighted(text_layer, alpha, frame, 1.0 - alpha, 0, frame)
def _save_current_frame(self) -> None:
if self._latest_frame is None:
return
captures_dir = Path("captures")
captures_dir.mkdir(exist_ok=True)
filename = datetime.now().strftime("capture_%Y%m%d_%H%M%S.jpg")
cv2.imwrite(str(captures_dir / filename), self._latest_frame)
self._saved_message_until = time.time() + 1.0
22

22 / Step

Membuat entry point aplikasi

File main.py adalah pintu masuk aplikasi. File ini membuat object webcam, hand detector, blur service, dan window OpenCV. Setelah semua object dibuat, aplikasi dijalankan dengan window.run().

23

23 / Step

Kode main.py

Kode main.py

main.py
from src.application.camera_blur_service import CameraBlurService
from src.infrastructure.mediapipe_hand_detector import MediaPipeHandDetector
from src.infrastructure.webcam_camera import WebcamCamera
from src.presentation.opencv_window import OpenCVWindow
def main() -> None:
camera = WebcamCamera(camera_index=0)
hand_detector = MediaPipeHandDetector()
blur_service = CameraBlurService()
window = OpenCVWindow(camera, hand_detector, blur_service)
try:
window.run()
finally:
hand_detector.close()
camera.release()
if __name__ == "__main__":
main()
24

24 / Step

Menjalankan aplikasi

Setelah semua file dibuat, install dependency terlebih dahulu. Setelah itu jalankan compile check untuk memastikan tidak ada error syntax. Jika aman, jalankan main.py untuk membuka aplikasi webcam.

25

25 / Step

Command menjalankan aplikasi

Command menjalankan aplikasi

terminal
py -m pip install -r requirements.txt
py -m compileall .
py main.py
26

26 / Step

Cara menggunakan aplikasi

  • Setelah window kamera terbuka, arahkan tangan ke kamera. Jika tangan terdeteksi, garis indikator akan muncul pada jari.
  • Tunjukkan peace sign atau dua jari. Jika gesture berhasil terbaca, tampilan kamera akan berubah menjadi blur dan garis pada telunjuk serta jari tengah akan berwarna hijau.
  • Tekan q untuk keluar dari aplikasi. Tekan s untuk menyimpan frame ke folder captures. Tekan b untuk mengaktifkan blur manual tanpa gesture. Tekan h untuk menampilkan atau menyembunyikan helper text.
27

27 / Step

Jika aplikasi belum berjalan

  • Jika kamera tidak muncul, pastikan tidak ada aplikasi lain yang sedang memakai webcam, seperti Zoom, OBS, browser, atau aplikasi kamera bawaan.
  • Jika kamera tetap tidak terbuka, ubah camera_index dari 0 menjadi 1.
  • Jika gesture tidak terbaca, pastikan tangan cukup terang, tidak terlalu dekat dengan kamera, dan posisi telunjuk serta jari tengah benar-benar terbuka.
  • Jika model MediaPipe gagal diunduh, download manual file hand_landmarker.task lalu simpan ke folder models.

Project Foto Kita Blur ini cocok untuk latihan awal computer vision karena fiturnya kecil tetapi lengkap. Dari project ini, kita belajar membaca webcam, mendeteksi tangan, mengenali gesture, memberi efek blur, menggambar indikator jari, dan menyimpan hasil capture.

Setelah memahami alurnya, kamu bisa mengembangkan aplikasi ini dengan efek visual lain, gesture tambahan, atau tampilan UI yang lebih menarik.

Bagaimana artikel ini?

Berikan rating untuk membantu kami menyajikan konten yang lebih baik

Klik bintang untuk menilai

5.0 / 5.0 (1 reviews)

Blog Terkait

Baca tulisan menarik lainnya dari kami

Lihat Semua