Tutorial Membuat Foto Kita Blur dengan Python
Haris Riyoni
Author

01 / Project Setup
Project Setup
Tools yang kamu butuhin sebelum mulai, dan perintah untuk install-nya.
Tools
1 item- https://www.python.org/downloads/release/python-3142/
Python
v3.14.2link
Install Steps
6 steps- 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
- 02
Buat struktur folder
Folder Models digunakan untuk menyimpan model program ketika kita menjalankan program di akhir, Buat Folder via Terminal :
$ mkdir modelssebagai pembungkus folder.
$ mkdir srcFolder domain berisi aturan utama.
$ mkdir src\domainApplication berisi logic aplikasi.
$ mkdir src\applicationinfrastructure berisi integrasi webcam dan MediaPipe.
$ mkdir src\infrastructureSedangkan presentation berisi tampilan OpenCV.
$ mkdir src\presentation
- 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
- 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
- 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 .
- 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.
- project
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 / 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 / Step
Isi requirements.txt
Isi requirements.txt
opencv-pythonmediapipenumpy06 / 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 / Step
Kode gesture.py
Kode gesture.py
from enum import Enumclass Gesture(str, Enum): NONE = "none" PEACE_SIGN = "peace_sign"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 / Step
Kode hand_detection.py
Kode hand_detection.py
from dataclasses import dataclassfrom 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) > 010 / 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 / Step
Kode interfaces.py
Kode interfaces.py
from abc import ABC, abstractmethodimport numpy as npfrom src.domain.hand_detection import HandDetectionclass Camera(ABC): @abstractmethod def read(self) -> tuple[bool, np.ndarray | None]: pass @abstractmethod def release(self) -> None: passclass HandDetector(ABC): @abstractmethod def detect(self, frame: np.ndarray) -> HandDetection: pass @abstractmethod def close(self) -> None: pass12 / 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 / Step
Kode webcam_camera.py
Kode webcam_camera.py
import cv2import numpy as npfrom src.domain.interfaces import Cameraclass 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 / 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 / Step
Kode deteksi peace sign
Kode deteksi peace sign
from pathlib import Pathfrom urllib.error import URLErrorfrom urllib.request import urlretrieveimport cv2import mediapipe as mpimport numpy as npfrom src.domain.gesture import Gesturefrom src.domain.hand_detection import HandDetectionfrom src.domain.interfaces import HandDetectorclass 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_PATH16 / 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 / 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 / Step
Kode camera_blur_service.py
Kode camera_blur_service.py
from dataclasses import dataclassimport cv2import numpy as npfrom src.domain.gesture import Gesture@dataclassclass CameraBlurState: blur_strength: float = 0.0 is_blur_active: bool = False manual_blur: bool = Falseclass 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 / 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 / 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 / Step
Kode indikator jari
Kode indikator jari
from __future__ import annotationsimport timefrom datetime import datetimefrom pathlib import Pathimport cv2import numpy as npfrom src.application.camera_blur_service import CameraBlurService, CameraBlurStatefrom src.domain.gesture import Gesturefrom src.domain.hand_detection import HandDetectionfrom src.domain.interfaces import Camera, HandDetectorclass 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.022 / 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 / Step
Kode main.py
Kode main.py
from src.application.camera_blur_service import CameraBlurServicefrom src.infrastructure.mediapipe_hand_detector import MediaPipeHandDetectorfrom src.infrastructure.webcam_camera import WebcamCamerafrom src.presentation.opencv_window import OpenCVWindowdef 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 / 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 / Step
Command menjalankan aplikasi
Command menjalankan aplikasi
py -m pip install -r requirements.txtpy -m compileall .py main.py26 / 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 / 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
Blog Terkait
Baca tulisan menarik lainnya dari kami


