بیاید یه هوش مصنوعی ساده و ۱۰۰ درصد برنده توی بازی سنگ کاغذ قیچی بسازیم
میخوایم چیکار کنیم : مثل خارجیا سه بار دستمون رو بالا پایین میاریم و بار سوم حرکتی رو که میخوایم میزنیم.هوش مصنوعی هم دقیقا موقعی که میخوایم حرکت رو بازی کنیم اون حرکت رو تشخیص میده و حرکتی رو میزنه که همیشه برنده باشه، به طوری که ذهن متوجه نشه که آقا تقلب کردن :)

خب بزارید این پروژه رو بشکنیم به بخش های کوچیک تا قدم به قدم انجامش بدیم.
در قدم اول ما نیاز داریم که کلا دست رو تشخیص بدیم.خوشبختانه یه کتابخونه قدرتمند و بی رقیب برای اینکار وجود داره : مدیاپایپ(یکی از پروژه های اوپن سورس گوگل)
# Initialize MediaPipe Hands.mp_hands = mp.solutions.handshands = mp_hands.Hands(static_image_mode=False,max_num_hands=1,min_detection_confidence=0.4,min_tracking_confidence=0.4,)
حالا ما دستمون رو تشخیص میدیم و بخش جذابش اینه که کتابخونه مدیاپایپ روی دست نقطه گذاری میکنه و مختصات نقاط رو هم به ما میده.

حالا وظیفه بعدی اینه که مختصات یکی از این نقاط رو توی ویدیو دنبال کنیم.و بررسی کنیم که اگر دست داشت پایین میومد و میزان پایین اومدنش از یه عددی بیشتر شد یدونه به شمارنده بازی(counter) اضافه کنیم.(توی این پروژه من مختصات wrist که نقطه پایین دست هست رو دنبال میکنم و اون عدد خاص هم بعد از یه سری آزمون و خطا 170 قرار دادم).الگوریتم زیر این کارو برای ما انجام میده :
counter = 0yHigh = 1000yChanges = 0y = 0if results.multi_hand_landmarks:for hand_landmarks in results.multi_hand_landmarks:landmarks = hand_landmarks.landmarkwrist = hand_landmarks.landmark[mp_hands.HandLandmark.WRIST]x, y = int(wrist.x * width), int(wrist.y * height)if y < yHigh:yHigh = yelse:yChanges = y - yHighif yChanges > 170:counter += 1yChanges = 0yHigh = 1000
خب الان شمارنده ما میشه 1 و 2 و وقتی که میخواد بشه 3 دقیقا لحظه ای هست که ما باید حرکتمون رو بین سنگ کاغذ قیچی شروع کنیم به نشون دادن و دقیقا از همین فریم عکس برداری میکنیم برای اینکه بدیمش به شبکه عصبی و پیش بینی کنیم که چه حرکتی هست.
و از اینجا تازه بخش چالشیمون شروع میشه.تصویر زیر یه مثال از تصویر برداری یک فریم هست که حرکت کاغذ رو نشون میده و مسلما میتونید حدس بزنید چرا این بخش چالشیه.

دو تا چالش اساسی راجع به این فریم وجود داره :
1-دست در حال حرکته و توی 90 درصد موارد فریمی که تصویر برداری میشه مثل بالا شفاف نیست و کار شبکه رو سخت میکنه
2-این بازی بایستی بتونه توی هر محیطی انجام بشه و مسلما هر محیطی بک گراند خاصی داره و این بک گراند میتونه توی پیش بینی های شبکه عصبی تاثیر منفی بزاره(که قطعا میزاره)
بعد از تلاش های فراوان و خروجی های مختلف و ناکارآمد یه راه حل جالب و قشنگ به ذهنم رسید.
راه حل درخشان بنده : با توجه به اینکه کتابخونه مدیاپایپ خیلی قدرتمنده و عملا بدون توجه به حرکت دست میتونه اون رو با دقت بالایی تشخیص بده و از طرفی روی دست نقطه گذاری میکنه، میتونیم از این قابلیت استفاده مثمرثمری داشته باشیم.اونم اینکه به جای تصویر خود دست، صرفا تصویر این نقاط رو برای آموزش شبکه عصبیمون استفاده کنیم.

خب بیاین با این روش قدم به قدم شبکمون رو آماده کنیم.
1-جمع کردن دیتاست: با کمک گرفتن از چت جی پی تی عزیزمون یه کد تمیز برای جمع کردن دیتاستی از نقاط دست فراهم کردیم.توی کد زیر مقدار IMG_SIZE عدد 5000 هست به این معنا که از هر حالت سنگ و کاغذ و قیچی 5000 تا نمونه قراره بگیریم.به این صورت که با شروع برنامه شما با انتخاب یکی از حروف s یا p یا r که حرف اول سنگ و کاغذ و قیچی هست(مسلما به انگلیسی دیگه :)) برنامه شروع به کار میکنه، دستتون رو میگیرید جلوش به صورتی که میخواید و بهتره که دستتون رو دائما حرکت بدید تا حالات مختلف یک وضعیت مثل سنگ نمونه برداری بشه و شبکه بتونه حالات بیشتری رو پیش بینی کنه.
import cv2import mediapipe as mpimport numpy as npimport os# Initialize Mediapipemp_hands = mp.solutions.handsmp_drawing = mp.solutions.drawing_utilshands = mp_hands.Hands(static_image_mode=False, max_num_hands=1, min_detection_confidence=0.7)# Create directories for storing the imagesos.makedirs('rock', exist_ok=True)os.makedirs('paper', exist_ok=True)os.makedirs('scissors', exist_ok=True)# Count of each gesture imageIMG_SIZE = 5000# Function to preprocess landmarks and save as imagedef preprocess_and_save_landmarks(landmarks, frame, save_path, img_size=128):# Get the frame dimensionsh, w, _ = frame.shape# Get the bounding box of the handx_min = min([landmark.x for landmark in landmarks])x_max = max([landmark.x for landmark in landmarks])y_min = min([landmark.y for landmark in landmarks])y_max = max([landmark.y for landmark in landmarks])# Calculate the center and size of the bounding boxx_center = (x_min + x_max) / 2y_center = (y_min + y_max) / 2bbox_size = max(x_max - x_min, y_max - y_min)# Calculate the scaling factor and translate the landmarksscale = img_size / bbox_sizeblack_bg = np.zeros((img_size, img_size, 3), dtype=np.uint8)for landmark in landmarks:x = int((landmark.x - x_center) * scale + img_size / 2)y = int((landmark.y - y_center) * scale + img_size / 2)cv2.circle(black_bg, (x, y), 2, (255, 0, 0), -1)cv2.imwrite(save_path, black_bg)# Open the webcamcap = cv2.VideoCapture(0)gesture = Noneimg_count = 0save_dir = ""while True:success, frame = cap.read()if not success:break# Flip the frame horizontally for a later selfie-view displayframe = cv2.flip(frame, 1)# Convert the BGR image to RGBrgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)# Process the frame and detect handsresults = hands.process(rgb_frame)# Display instructionsif gesture is None:cv2.putText(frame, "Press 'r' for Rock, 'p' for Paper, 's' for Scissors",(10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2, cv2.LINE_AA)else:cv2.putText(frame, f"Capturing {gesture} images: {img_count}/5000",(10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2, cv2.LINE_AA)if results.multi_hand_landmarks and gesture is not None and img_count < IMG_SIZE:for hand_landmarks in results.multi_hand_landmarks:# Draw hand landmarks on the framemp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)# Extract the landmarkslandmarks = hand_landmarks.landmark# Save the preprocessed landmarks as an imagesave_path = os.path.join(save_dir, f"{gesture}_{img_count:04d}.png")preprocess_and_save_landmarks(landmarks, frame, save_path)img_count += 1if img_count >= IMG_SIZE:gesture = Noneimg_count = 0# Display the framecv2.imshow('Capture Hand Gestures', frame)# Handle keypress eventskey = cv2.waitKey(1)if key == ord('r'):gesture = 'rock'save_dir = 'rock'elif key == ord('p'):gesture = 'paper'save_dir = 'paper'elif key == ord('s'):gesture = 'scissors'save_dir = 'scissors'elif key == ord('q'):break# Release the webcam and close the windowcap.release()cv2.destroyAllWindows()
2-ساخت و آموزش شبکه عصبی
باز به پیشنهاد چت جی پی تی جان مدلی با ساختار زیر رو ساختیم :

و بعد از آموزش این شبکه با 12000 تصویر آموزشی و تست اون با 3000 تصویر تستی به دقت 100 درصد توی هر دو رسیدیم.

و عملا کار دیگه جَمعه...
تنها کاری که مونده اینه که مدلمون رو سیو کنیم و توی برنامه اصلی از اون برای پیش بینی حالت دست وقتی که شمارنده به 3 میرسه استفاده کنیم و متناسب با اون حرکت هوش مصنوعی رو مشخص کنیم.
به همین سادگی و خوشمزگی. اگر دوست داشتین نسخه کامل کد رو ببنید به گیت هاب بنده سری بزنید.
ضمن اینکه اگر میخواید نسخه ویدیوییش رو هم ببنید میتونید این ویدیو رو از کانال یوتیوب من مشاهده کنید.
ممنون از وقتی که برای مطالعه گذاشتید...
باکیفیت ترین دوره آموزش برنامه نویسی چیست ؟
⭐⭐⭐⭐⭐ دوره ی آموزش دوازده قدم برنامه نویسی مهندس سام نیک زاد با کسب امتیاز بالاترین کیفیت دوره ی آموزشی بعنوان با کیفیت دوره انتخاب شده است برای مشاهده کلیک کنید
ثبت نام دوره با تخفیف از ویرگول
مطلبی دیگر از این انتشارات
UUID vs. Sequential ID as Primary Key
مطلبی دیگر از این انتشارات
چی شد که این شد!
مطلبی دیگر از این انتشارات
چهارصد بار سریع تر داده ها رو بخوانید (فایل باینری)