diff --git a/CardDetector.py b/CardDetector.py index 136a8be..a24068d 100644 --- a/CardDetector.py +++ b/CardDetector.py @@ -1,124 +1,702 @@ -############## Python-OpenCV Playing Card Detector ############### -# -# Author: Evan Juras -# Date: 9/5/17 -# Description: Python script to detect and identify playing cards -# from a PiCamera video feed. -# - -# Import necessary packages -import cv2 -import numpy as np -import time -import os -import Cards -import VideoStream - - -### ---- INITIALIZATION ---- ### -# Define constants and initialize variables - -## Camera settings -IM_WIDTH = 1280 -IM_HEIGHT = 720 -FRAME_RATE = 10 - -## Initialize calculated frame rate because it's calculated AFTER the first time it's displayed -frame_rate_calc = 1 -freq = cv2.getTickFrequency() - -## Define font to use -font = cv2.FONT_HERSHEY_SIMPLEX - -# Initialize camera object and video feed from the camera. The video stream is set up -# as a seperate thread that constantly grabs frames from the camera feed. -# See VideoStream.py for VideoStream class definition -## IF USING USB CAMERA INSTEAD OF PICAMERA, -## CHANGE THE THIRD ARGUMENT FROM 1 TO 2 IN THE FOLLOWING LINE: -videostream = VideoStream.VideoStream((IM_WIDTH,IM_HEIGHT),FRAME_RATE,1,0).start() -time.sleep(1) # Give the camera time to warm up - -# Load the train rank and suit images -path = os.path.dirname(os.path.abspath(__file__)) -train_ranks = Cards.load_ranks( path + '/Card_Imgs/') -train_suits = Cards.load_suits( path + '/Card_Imgs/') - - -### ---- MAIN LOOP ---- ### -# The main loop repeatedly grabs frames from the video stream -# and processes them to find and identify playing cards. - -cam_quit = 0 # Loop control variable - -# Begin capturing frames -while cam_quit == 0: - - # Grab frame from video stream - image = videostream.read() - - # Start timer (for calculating frame rate) - t1 = cv2.getTickCount() - - # Pre-process camera image (gray, blur, and threshold it) - pre_proc = Cards.preprocess_image(image) - - # Find and sort the contours of all cards in the image (query cards) - cnts_sort, cnt_is_card = Cards.find_cards(pre_proc) - - # If there are no contours, do nothing - if len(cnts_sort) != 0: - - # Initialize a new "cards" list to assign the card objects. - # k indexes the newly made array of cards. - cards = [] - k = 0 - - # For each contour detected: - for i in range(len(cnts_sort)): - if (cnt_is_card[i] == 1): - - # Create a card object from the contour and append it to the list of cards. - # preprocess_card function takes the card contour and contour and - # determines the cards properties (corner points, etc). It generates a - # flattened 200x300 image of the card, and isolates the card's - # suit and rank from the image. - cards.append(Cards.preprocess_card(cnts_sort[i],image)) - - # Find the best rank and suit match for the card. - cards[k].best_rank_match,cards[k].best_suit_match,cards[k].rank_diff,cards[k].suit_diff = Cards.match_card(cards[k],train_ranks,train_suits) - - # Draw center point and match result on the image. - image = Cards.draw_results(image, cards[k]) - k = k + 1 - - # Draw card contours on image (have to do contours all at once or - # they do not show up properly for some reason) - if (len(cards) != 0): - temp_cnts = [] - for i in range(len(cards)): - temp_cnts.append(cards[i].contour) - cv2.drawContours(image,temp_cnts, -1, (255,0,0), 2) - - - # Draw framerate in the corner of the image. Framerate is calculated at the end of the main loop, - # so the first time this runs, framerate will be shown as 0. - cv2.putText(image,"FPS: "+str(int(frame_rate_calc)),(10,26),font,0.7,(255,0,255),2,cv2.LINE_AA) - - # Finally, display the image with the identified cards! - cv2.imshow("Card Detector",image) - - # Calculate framerate - t2 = cv2.getTickCount() - time1 = (t2-t1)/freq - frame_rate_calc = 1/time1 - - # Poll the keyboard. If 'q' is pressed, exit the main loop. - key = cv2.waitKey(1) & 0xFF - if key == ord("q"): - cam_quit = 1 - - -# Close all windows and close the PiCamera video stream. -cv2.destroyAllWindows() -videostream.stop() - +############## Python-OpenCV Playing Card Detector ############### +# +# Author: Evan Juras +# Date: 9/5/17 +# Description: Python script to detect and identify playing cards +# from a PiCamera video feed. Updated with blackjack helpers, +# outcome evaluation, and cleaner overlays. +# + +import argparse +import os +import time +from typing import Dict, List, Optional, Tuple, TypedDict + +import cv2 +import numpy as np + +import Cards +import VideoStream + + +### ---- INITIALIZATION ---- ### +# Define constants and initialize variables + +## Camera settings +IM_WIDTH = 1280 +IM_HEIGHT = 720 +FRAME_RATE = 10 + +## Define font to use +font = cv2.FONT_HERSHEY_SIMPLEX + + +class Detection(TypedDict): + rank: str + rank_diff: int + center: Tuple[int, int] + + +class TrackedCardDisplay(TypedDict): + rank: str + center: Tuple[int, int] + is_stable: bool + + +def _parse_cli_args() -> argparse.Namespace: + """Parse command-line arguments for detector configuration.""" + parser = argparse.ArgumentParser(description="Playing card detector camera settings") + parser.add_argument( + "--camera-type", + choices=["auto", "pi", "usb"], + default="auto", + help="Select camera backend: 'pi' for PiCamera, 'usb' for USB/Continuity Camera, 'auto' tries Pi then USB.", + ) + parser.add_argument( + "--camera-index", + type=int, + default=0, + help="USB camera index (ignored for PiCamera). Use when multiple cameras are connected.", + ) + return parser.parse_args() + + +def _create_videostream(args: argparse.Namespace) -> VideoStream.VideoStream: + """Create a video stream that matches the requested backend.""" + if args.camera_type == "pi": + return VideoStream.VideoStream((IM_WIDTH, IM_HEIGHT), FRAME_RATE, 1, 0).start() + if args.camera_type == "usb": + return VideoStream.VideoStream((IM_WIDTH, IM_HEIGHT), FRAME_RATE, 2, args.camera_index).start() + try: + return VideoStream.VideoStream((IM_WIDTH, IM_HEIGHT), FRAME_RATE, 1, 0).start() + except ModuleNotFoundError: + print("picamera module not found; defaulting to USB camera.") + return VideoStream.VideoStream((IM_WIDTH, IM_HEIGHT), FRAME_RATE, 2, args.camera_index).start() + + +### ---- BLACKJACK HELPERS ---- ### + +_FACE_RANKS = {"Ten", "Jack", "Queen", "King"} +DEALER_ZONE_RATIO = 0.4 # y-position threshold that separates dealer (top) from player (bottom) +MAX_TRACK_DISTANCE = 90.0 # pixels +MIN_VOTES_FOR_STABLE = 3 +MAX_MISSED_FRAMES = 40 +RANK_DIFF_ACCEPTANCE = 1400 # lower is stricter; tweak based on noise +RECOUNT_MISS_THRESHOLD = 15 # frames a card must disappear before recounting + + +def _rank_to_blackjack_value(rank: str) -> Optional[int]: + """Map detector rank label to its blackjack numeric value.""" + if rank == "Unknown": + return None + if rank == "Ace": + return 11 + if rank in _FACE_RANKS: + return 10 + text_to_value = { + "Two": 2, + "Three": 3, + "Four": 4, + "Five": 5, + "Six": 6, + "Seven": 7, + "Eight": 8, + "Nine": 9, + } + return text_to_value.get(rank) + + +def _pair_value(rank: str) -> Optional[int]: + """Return pair comparison value for rank names.""" + if rank == "Unknown": + return None + if rank == "Ace": + return 11 + if rank in _FACE_RANKS: + return 10 + return _rank_to_blackjack_value(rank) + + +def _hand_total_and_soft(ranks: List[str]) -> Tuple[int, bool]: + """Calculate best blackjack total and whether the hand is soft.""" + total = 0 + ace_as_eleven = 0 + for rank in ranks: + value = _rank_to_blackjack_value(rank) + if value is None: + continue + if rank == "Ace": + ace_as_eleven += 1 + total += value + aces_remaining_as_11 = ace_as_eleven + while total > 21 and aces_remaining_as_11 > 0: + total -= 10 + aces_remaining_as_11 -= 1 + is_soft = aces_remaining_as_11 > 0 + return total, is_soft + + +def _basic_strategy_action(player_ranks: List[str], dealer_rank: Optional[str]) -> str: + """ + Return basic-strategy move for multi-deck blackjack (dealer stands on soft 17). + Possible returns: Hit, Stand, Double, Split, or Waiting for cards. + """ + #small change + if len(player_ranks) < 2 or not dealer_rank or dealer_rank == "Unknown": + return "Waiting for cards" + + dealer_value = _rank_to_blackjack_value(dealer_rank) + if dealer_value is None: + return "Waiting for cards" + + two_card_hand = len(player_ranks) == 2 + pair_values = {_pair_value(rank) for rank in player_ranks} + if two_card_hand and len(pair_values) == 1: + pair_val = pair_values.pop() + if pair_val is not None: + if pair_val == 11: # A,A + return "Split" + if pair_val == 10: # Tens + return "Stand" + if pair_val == 9: + return "Split" if dealer_value in {2, 3, 4, 5, 6, 8, 9} else "Stand" + if pair_val == 8: + return "Split" + if pair_val == 7: + return "Split" if dealer_value in {2, 3, 4, 5, 6, 7} else "Hit" + if pair_val == 6: + return "Split" if dealer_value in {2, 3, 4, 5, 6} else "Hit" + if pair_val == 4: + return "Split" if dealer_value in {5, 6} else "Hit" + if pair_val in {2, 3}: + return "Split" if dealer_value in {2, 3, 4, 5, 6, 7} else "Hit" + if pair_val == 5: + if dealer_value in {2, 3, 4, 5, 6, 7, 8, 9}: + return "Double" + return "Hit" + + total, is_soft = _hand_total_and_soft(player_ranks) + + if is_soft: + if total >= 20: + return "Stand" + if total == 19: + if two_card_hand and dealer_value == 6: + return "Double" + return "Stand" + if total == 18: + if two_card_hand and dealer_value in {3, 4, 5, 6}: + return "Double" + if dealer_value in {2, 7, 8}: + return "Stand" + return "Hit" + if total == 17: + if two_card_hand and dealer_value in {3, 4, 5, 6}: + return "Double" + return "Hit" + if total in {15, 16}: + if two_card_hand and dealer_value in {4, 5, 6}: + return "Double" + return "Hit" + if total in {13, 14}: + if two_card_hand and dealer_value in {5, 6}: + return "Double" + return "Hit" + return "Hit" + + if total >= 17: + return "Stand" + if total == 16: + return "Stand" if dealer_value in {2, 3, 4, 5, 6} else "Hit" + if total == 15: + return "Stand" if dealer_value in {2, 3, 4, 5, 6} else "Hit" + if total == 14: + return "Stand" if dealer_value in {2, 3, 4, 5, 6} else "Hit" + if total == 13: + return "Stand" if dealer_value in {2, 3, 4, 5, 6} else "Hit" + if total == 12: + return "Stand" if dealer_value in {4, 5, 6} else "Hit" + if total == 11: + if two_card_hand and dealer_value != 11: + return "Double" + return "Hit" + if total == 10: + if two_card_hand and dealer_value in {2, 3, 4, 5, 6, 7, 8, 9}: + return "Double" + return "Hit" + if total == 9: + if two_card_hand and dealer_value in {3, 4, 5, 6}: + return "Double" + return "Hit" + return "Hit" + + +def _match_rank_only(qcard: Cards.Query_card, rank_library: List[Cards.Train_ranks]) -> Tuple[str, int]: + """Return best rank match and diff, ignoring suits.""" + best_rank_name = "Unknown" + best_rank_diff = Cards.RANK_DIFF_MAX + if len(qcard.rank_img) != 0: + for train_rank in rank_library: + diff_img = cv2.absdiff(qcard.rank_img, train_rank.img) + rank_diff = int(np.sum(diff_img) / 255) + if rank_diff < best_rank_diff: + best_rank_diff = rank_diff + best_rank_name = train_rank.name + return best_rank_name, best_rank_diff + + +def _draw_rank_result(image: np.ndarray, center: Tuple[int, int], label: str) -> None: + """Overlay rank label and center point without suits.""" + x_center, y_center = center + cv2.circle(image, (x_center, y_center), 5, (255, 0, 0), -1) + cv2.putText(image, label, (x_center - 50, y_center), font, 1, (0, 0, 0), 3, cv2.LINE_AA) + cv2.putText(image, label, (x_center - 50, y_center), font, 1, (0, 255, 255), 2, cv2.LINE_AA) + + +def _format_card_string(rank: str) -> str: + """Produce a short description of a card for on-screen text.""" + if rank == "Unknown": + return "Unknown" + return rank[:3].upper() + + +def _detect_cards( + image: np.ndarray, + rank_library: List[Cards.Train_ranks], + frame_height: int, +) -> Tuple[List[Detection], List[Detection]]: + """Locate cards in the frame and return dealer/player detections.""" + pre_proc = Cards.preprocess_image(image) + cnts_sort, cnt_is_card = Cards.find_cards(pre_proc) + + player_detections: List[Detection] = [] + dealer_detections: List[Detection] = [] + cards_in_frame: List[Cards.Query_card] = [] + + if cnts_sort: + for contour, is_card in zip(cnts_sort, cnt_is_card): + if not is_card: + continue + qcard = Cards.preprocess_card(contour, image) + best_rank, rank_diff = _match_rank_only(qcard, rank_library) + qcard.best_rank_match = best_rank + qcard.rank_diff = rank_diff + qcard.best_suit_match = "Unknown" + qcard.suit_diff = 0 + + detection: Detection = { + "rank": best_rank, + "rank_diff": rank_diff, + "center": (int(qcard.center[0]), int(qcard.center[1])), + } + if detection["center"][1] <= frame_height * DEALER_ZONE_RATIO: + dealer_detections.append(detection) + else: + player_detections.append(detection) + cards_in_frame.append(qcard) + + if cards_in_frame: + cv2.drawContours(image, [card.contour for card in cards_in_frame], -1, (255, 0, 0), 2) + + return player_detections, dealer_detections + + +def _stable_known_ranks(cards: List[TrackedCardDisplay]) -> List[str]: + """Return ranks for cards that have stabilized and are recognized.""" + return [card["rank"] for card in cards if card["is_stable"] and card["rank"] != "Unknown"] + + +def _card_summary(cards: List[TrackedCardDisplay]) -> str: + """Create a compact text summary of tracked cards.""" + text = " ".join(_format_card_string(card["rank"]) for card in cards) + return text if text else "None" + + +def _blend_rect( + image: np.ndarray, + top_left: Tuple[int, int], + bottom_right: Tuple[int, int], + color: Tuple[int, int, int], + alpha: float = 0.55, +) -> None: + """Draw a tinted rectangle without losing scene context.""" + overlay = image.copy() + cv2.rectangle(overlay, top_left, bottom_right, color, -1) + cv2.addWeighted(overlay, alpha, image, 1 - alpha, 0, image) + + +def _draw_label_value( + image: np.ndarray, + label: str, + value: str, + origin: Tuple[int, int], + label_color: Tuple[int, int, int], + value_color: Tuple[int, int, int], + value_scale: float = 0.8, +) -> None: + """Render a two-line label/value pair with consistent spacing.""" + x, y = origin + cv2.putText(image, label, (x, y), font, 0.55, label_color, 1, cv2.LINE_AA) + cv2.putText(image, value, (x, y + 28), font, value_scale, value_color, 2, cv2.LINE_AA) + + +def _determine_blackjack_outcome( + player_ranks: List[str], + dealer_ranks: List[str], + player_total: Optional[int], + dealer_total: Optional[int], +) -> Tuple[str, Tuple[int, int]]: + """Return a human-readable outcome string and display color.""" + if player_total is None or dealer_total is None: + return "Waiting for cards", (0, 200, 255) + + player_blackjack = len(player_ranks) == 2 and player_total == 21 + dealer_blackjack = len(dealer_ranks) == 2 and dealer_total == 21 + + if player_total > 21 and dealer_total > 21: + return "Both bust", (0, 180, 255) + if player_total > 21: + return "Dealer wins (player busts)", (0, 0, 255) + if dealer_total > 21: + return "Player wins (dealer busts)", (0, 220, 0) + if player_blackjack and dealer_blackjack: + return "Push (blackjack)", (0, 200, 255) + if player_blackjack: + return "Player wins (blackjack)", (0, 220, 0) + if dealer_blackjack: + return "Dealer wins (blackjack)", (0, 0, 255) + if player_total > dealer_total: + return "Player wins", (0, 220, 0) + if player_total < dealer_total: + return "Dealer wins", (0, 0, 255) + return "Push", (0, 200, 255) + + +def _render_overlay( + image: np.ndarray, + frame_rate_calc: float, + player_cards: List[TrackedCardDisplay], + dealer_cards: List[TrackedCardDisplay], + player_total: Optional[int], + player_soft: bool, + dealer_upcard: Optional[str], + recommendation: str, + outcome_message: str, + outcome_color: Tuple[int, int, int], +) -> None: + """Render the informational overlay on the frame.""" + player_cards_text = _card_summary(player_cards) + dealer_cards_text = _card_summary(dealer_cards) + total_label = "?" + if player_total is not None: + total_label = f"{player_total} ({'soft' if player_soft else 'hard'})" + dealer_label = dealer_upcard if dealer_upcard else "?" + + if recommendation == "Waiting for cards": + action_color = (0, 200, 255) + elif recommendation in {"Stand", "Double", "Split"}: + action_color = (0, 220, 0) + else: + action_color = (0, 0, 255) + + height, width = image.shape[:2] + banner_height = 70 + panel_left = 20 + panel_width = min(int(width * 0.42), 430) + panel_top = banner_height + 20 + panel_height = 210 + stats_top = panel_top + panel_height + 16 + stats_height = 150 + panel_right = panel_left + panel_width + + _blend_rect(image, (0, 0), (width, banner_height), outcome_color, alpha=0.6) + _blend_rect(image, (panel_left, panel_top), (panel_right, panel_top + panel_height), (30, 30, 30), alpha=0.7) + _blend_rect(image, (panel_left, stats_top), (panel_right, stats_top + stats_height), (22, 22, 22), alpha=0.7) + + banner_text = outcome_message.upper() + cv2.putText(image, banner_text, (26, 45), font, 1.1, (255, 255, 255), 3, cv2.LINE_AA) + fps_text = f"{frame_rate_calc:.1f} FPS" + cv2.putText(image, fps_text, (width - 180, 42), font, 0.65, (255, 255, 255), 2, cv2.LINE_AA) + + text_left = panel_left + 22 + dealer_origin_y = panel_top + 42 + _draw_label_value( + image, + "DEALER", + dealer_cards_text, + (text_left, dealer_origin_y), + (210, 230, 255), + (255, 255, 255), + ) + cv2.putText( + image, + f"Upcard: {dealer_label}", + (text_left, dealer_origin_y + 62), + font, + 0.6, + (210, 210, 210), + 1, + cv2.LINE_AA, + ) + + player_origin_y = dealer_origin_y + 95 + _draw_label_value( + image, + "PLAYER", + player_cards_text, + (text_left, player_origin_y), + (185, 255, 200), + (255, 255, 255), + ) + cv2.putText( + image, + f"Total: {total_label}", + (text_left, player_origin_y + 62), + font, + 0.65, + (220, 220, 220), + 2, + cv2.LINE_AA, + ) + + stats_origin_y = stats_top + 38 + _draw_label_value( + image, + "PLAY", + recommendation.upper(), + (text_left, stats_origin_y), + (210, 230, 255), + action_color, + value_scale=0.9, + ) + + counts_y = stats_origin_y + 55 + cv2.putText( + image, + f"Player cards {len(player_cards)}", + (text_left, counts_y), + font, + 0.6, + (210, 210, 210), + 2, + cv2.LINE_AA, + ) + cv2.putText( + image, + f"Dealer cards {len(dealer_cards)}", + (text_left + 200, counts_y), + font, + 0.6, + (210, 210, 210), + 2, + cv2.LINE_AA, + ) + + counts_y += 30 + cv2.putText( + image, + "Press Q to quit", + (text_left, counts_y), + font, + 0.6, + (180, 220, 255), + 2, + cv2.LINE_AA, + ) + cv2.putText( + image, + "Press R to reset", + (text_left + 200, counts_y), + font, + 0.6, + (180, 220, 255), + 2, + cv2.LINE_AA, + ) + + +class CardTracker: + """Tracks card positions across frames to reduce recognition flicker.""" + + def __init__(self) -> None: + self.tracks: List[Dict[str, object]] = [] + + def update(self, detections: List[Detection]) -> Tuple[List[TrackedCardDisplay], List[str]]: + # Age existing tracks + for track in self.tracks: + track["missed"] += 1 + if "counted" not in track: + track["counted"] = False + if "last_counted_rank" not in track: + track["last_counted_rank"] = None + if track["missed"] >= RECOUNT_MISS_THRESHOLD and track.get("stable_rank") not in (None, "Unknown"): + track["stable_rank"] = "Unknown" + track["counted"] = False + track["last_counted_rank"] = None + track["candidate_rank"] = None + track["candidate_count"] = 0 + + newly_counted: List[str] = [] + + for det in detections: + center_arr = np.array(det["center"], dtype=np.float32) + rank = det["rank"] + rank_diff = det["rank_diff"] + + best_track = None + best_distance = MAX_TRACK_DISTANCE + for track in self.tracks: + distance = np.linalg.norm(center_arr - track["center"]) + if distance < best_distance: + best_distance = distance + best_track = track + + if best_track is not None and best_distance <= MAX_TRACK_DISTANCE: + prev_missed = best_track["missed"] + best_track["center"] = 0.7 * best_track["center"] + 0.3 * center_arr + best_track["missed"] = 0 + if rank != "Unknown" and rank_diff < RANK_DIFF_ACCEPTANCE: + if rank == best_track.get("candidate_rank"): + best_track["candidate_count"] += 1 + else: + best_track["candidate_rank"] = rank + best_track["candidate_count"] = 1 + if best_track["candidate_count"] >= MIN_VOTES_FOR_STABLE: + if best_track.get("stable_rank") != rank: + best_track["stable_rank"] = rank + if prev_missed >= RECOUNT_MISS_THRESHOLD: + best_track["counted"] = False + best_track["last_counted_rank"] = None + elif best_track.get("last_counted_rank") == rank: + best_track["counted"] = True + else: + best_track["counted"] = True + else: + best_track["candidate_rank"] = None + best_track["candidate_count"] = 0 + if best_track.get("stable_rank") not in (None, "Unknown") and not best_track["counted"]: + newly_counted.append(best_track["stable_rank"]) + best_track["counted"] = True + best_track["last_counted_rank"] = best_track["stable_rank"] + else: + new_track = { + "center": center_arr, + "stable_rank": "Unknown", + "candidate_rank": None, + "candidate_count": 0, + "missed": 0, + "counted": False, + "last_counted_rank": None, + } + if rank != "Unknown" and rank_diff < RANK_DIFF_ACCEPTANCE: + new_track["candidate_rank"] = rank + new_track["candidate_count"] = 1 + if new_track["candidate_count"] >= MIN_VOTES_FOR_STABLE: + new_track["stable_rank"] = rank + newly_counted.append(rank) + new_track["counted"] = True + new_track["last_counted_rank"] = rank + self.tracks.append(new_track) + + self.tracks = [track for track in self.tracks if track["missed"] <= MAX_MISSED_FRAMES] + + return self.get_cards(), newly_counted + + def get_cards(self) -> List[TrackedCardDisplay]: + cards: List[TrackedCardDisplay] = [] + for track in self.tracks: + stable_rank = track.get("stable_rank", "Unknown") + candidate_rank = track.get("candidate_rank") + display_rank = stable_rank if stable_rank != "Unknown" else (candidate_rank or "Unknown") + center_tuple = (int(track["center"][0]), int(track["center"][1])) + cards.append( + { + "rank": display_rank, + "center": center_tuple, + "is_stable": stable_rank != "Unknown", + } + ) + return sorted(cards, key=lambda card: card["center"][0]) + + +def main() -> None: + args = _parse_cli_args() + videostream = _create_videostream(args) + time.sleep(1) # Give the camera time to warm up + + path = os.path.dirname(os.path.abspath(__file__)) + train_ranks = Cards.load_ranks(os.path.join(path, "Card_Imgs") + "/") + + player_tracker = CardTracker() + dealer_tracker = CardTracker() + + frame_rate_calc = 1.0 + freq = cv2.getTickFrequency() + + try: + while True: + image = videostream.read() + frame_height = image.shape[0] + + t1 = cv2.getTickCount() + + player_detections, dealer_detections = _detect_cards(image, train_ranks, frame_height) + + dealer_cards_display, _ = dealer_tracker.update(dealer_detections) + player_cards_display, _ = player_tracker.update(player_detections) + + for tracked_card in dealer_cards_display + player_cards_display: + if tracked_card["rank"] != "Unknown": + _draw_rank_result(image, tracked_card["center"], tracked_card["rank"]) + + player_ranks = _stable_known_ranks(player_cards_display) + dealer_ranks = _stable_known_ranks(dealer_cards_display) + + player_total: Optional[int] = None + player_soft = False + if player_ranks: + player_total, player_soft = _hand_total_and_soft(player_ranks) + dealer_total: Optional[int] = None + if dealer_ranks: + dealer_total, _ = _hand_total_and_soft(dealer_ranks) + dealer_upcard = dealer_ranks[0] if dealer_ranks else None + recommendation = _basic_strategy_action(player_ranks, dealer_upcard) + + outcome_message, outcome_color = _determine_blackjack_outcome( + player_ranks, dealer_ranks, player_total, dealer_total + ) + + _render_overlay( + image, + frame_rate_calc, + player_cards_display, + dealer_cards_display, + player_total, + player_soft, + dealer_upcard, + recommendation, + outcome_message, + outcome_color, + ) + + cv2.imshow("Card Detector", image) + + t2 = cv2.getTickCount() + time1 = (t2 - t1) / freq + if time1 != 0: + frame_rate_calc = 1 / time1 + + key = cv2.waitKey(1) & 0xFF + if key == ord("q"): + break + if key == ord("r"): + player_tracker = CardTracker() + dealer_tracker = CardTracker() + + finally: + cv2.destroyAllWindows() + videostream.stop() + + +if __name__ == "__main__": + main() diff --git a/Cards.py b/Cards.py index 68c0cdb..298d9a7 100644 --- a/Cards.py +++ b/Cards.py @@ -132,7 +132,12 @@ def find_cards(thresh_image): from largest to smallest.""" # Find contours and sort their indices by contour size - dummy,cnts,hier = cv2.findContours(thresh_image,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) + contours = cv2.findContours(thresh_image,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) + if len(contours) == 3: + dummy,cnts,hier = contours + else: # OpenCV 4 compatibility + cnts,hier = contours + dummy = None index_sort = sorted(range(len(cnts)), key=lambda i : cv2.contourArea(cnts[i]),reverse=True) # If there are no contours, do nothing @@ -212,7 +217,12 @@ def preprocess_card(contour, image): Qsuit = query_thresh[186:336, 0:128] # Find rank contour and bounding rectangle, isolate and find largest contour - dummy, Qrank_cnts, hier = cv2.findContours(Qrank, cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) + contours = cv2.findContours(Qrank, cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) + if len(contours) == 3: + dummy, Qrank_cnts, hier = contours + else: + Qrank_cnts, hier = contours + dummy = None Qrank_cnts = sorted(Qrank_cnts, key=cv2.contourArea,reverse=True) # Find bounding rectangle for largest contour, use it to resize query rank @@ -224,7 +234,12 @@ def preprocess_card(contour, image): qCard.rank_img = Qrank_sized # Find suit contour and bounding rectangle, isolate and find largest contour - dummy, Qsuit_cnts, hier = cv2.findContours(Qsuit, cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) + contours = cv2.findContours(Qsuit, cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) + if len(contours) == 3: + dummy, Qsuit_cnts, hier = contours + else: + Qsuit_cnts, hier = contours + dummy = None Qsuit_cnts = sorted(Qsuit_cnts, key=cv2.contourArea,reverse=True) # Find bounding rectangle for largest contour, use it to resize query suit diff --git a/Rank_Suit_Isolator.py b/Rank_Suit_Isolator.py index bc6bbca..466ae22 100644 --- a/Rank_Suit_Isolator.py +++ b/Rank_Suit_Isolator.py @@ -3,6 +3,7 @@ ### Runs through A - K ranks and then the 4 suits. # Import necessary packages +import argparse import cv2 import numpy as np import time @@ -20,23 +21,39 @@ SUIT_WIDTH = 70 SUIT_HEIGHT = 100 -# If using a USB Camera instead of a PiCamera, change PiOrUSB to 2 -PiOrUSB = 1 - -if PiOrUSB == 1: - # Import packages from picamera library - from picamera.array import PiRGBArray - from picamera import PiCamera - - # Initialize PiCamera and grab reference to the raw capture - camera = PiCamera() - camera.resolution = (IM_WIDTH,IM_HEIGHT) - camera.framerate = 10 - rawCapture = PiRGBArray(camera, size=(IM_WIDTH,IM_HEIGHT)) - -if PiOrUSB == 2: - # Initialize USB camera - cap = cv2.VideoCapture(0) +parser = argparse.ArgumentParser(description="Rank/Suit isolator camera settings") +parser.add_argument("--camera-type", choices=["auto","pi","usb"], default="auto", + help="Select camera backend: 'pi' for PiCamera, 'usb' for USB/Continuity Camera, 'auto' tries Pi then USB.") +parser.add_argument("--camera-index", type=int, default=0, + help="USB camera index (ignored for PiCamera). Use when multiple cameras are connected.") +args = parser.parse_args() + +camera = None +rawCapture = None +cap = None +camera_type_used = None + +if args.camera_type in ("pi", "auto"): + try: + from picamera.array import PiRGBArray + from picamera import PiCamera + camera = PiCamera() + camera.resolution = (IM_WIDTH,IM_HEIGHT) + camera.framerate = 10 + rawCapture = PiRGBArray(camera, size=(IM_WIDTH,IM_HEIGHT)) + camera_type_used = "pi" + except ModuleNotFoundError: + if args.camera_type == "pi": + raise + print("picamera module not found; defaulting to USB camera.") + +if camera_type_used != "pi": + cap = cv2.VideoCapture(args.camera_index) + if not cap.isOpened(): + raise RuntimeError(f"Unable to open USB camera at index {args.camera_index}.") + cap.set(cv2.CAP_PROP_FRAME_WIDTH, IM_WIDTH) + cap.set(cv2.CAP_PROP_FRAME_HEIGHT, IM_HEIGHT) + camera_type_used = "usb" # Use counter variable to switch from isolating Rank to isolating Suit i = 1 @@ -51,7 +68,7 @@ - if PiOrUSB == 1: # PiCamera + if camera_type_used == "pi": # PiCamera rawCapture.truncate(0) # Press 'p' to take a picture for frame in camera.capture_continuous(rawCapture, format="bgr",use_video_port=True): @@ -64,7 +81,7 @@ rawCapture.truncate(0) - if PiOrUSB == 2: # USB camera + if camera_type_used == "usb": # USB camera # Press 'p' to take a picture while(True): @@ -81,7 +98,12 @@ retval, thresh = cv2.threshold(blur,100,255,cv2.THRESH_BINARY) # Find contours and sort them by size - dummy,cnts,hier = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) + contours = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) + if len(contours) == 3: + dummy,cnts,hier = contours + else: + cnts,hier = contours + dummy = None cnts = sorted(cnts, key=cv2.contourArea,reverse=True) # Assume largest contour is the card. If there are no contours, print an error @@ -114,7 +136,12 @@ # Isolate suit or rank if i <= 13: # Isolate rank rank = corner_thresh[20:185, 0:128] # Grabs portion of image that shows rank - dummy, rank_cnts, hier = cv2.findContours(rank, cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) + contours = cv2.findContours(rank, cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) + if len(contours) == 3: + dummy, rank_cnts, hier = contours + else: + rank_cnts, hier = contours + dummy = None rank_cnts = sorted(rank_cnts, key=cv2.contourArea,reverse=True) x,y,w,h = cv2.boundingRect(rank_cnts[0]) rank_roi = rank[y:y+h, x:x+w] @@ -123,7 +150,12 @@ if i > 13: # Isolate suit suit = corner_thresh[186:336, 0:128] # Grabs portion of image that shows suit - dummy, suit_cnts, hier = cv2.findContours(suit, cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) + contours = cv2.findContours(suit, cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) + if len(contours) == 3: + dummy, suit_cnts, hier = contours + else: + suit_cnts, hier = contours + dummy = None suit_cnts = sorted(suit_cnts, key=cv2.contourArea,reverse=True) x,y,w,h = cv2.boundingRect(suit_cnts[0]) suit_roi = suit[y:y+h, x:x+w] @@ -141,4 +173,7 @@ i = i + 1 cv2.destroyAllWindows() -camera.close() +if camera_type_used == "pi": + camera.close() +elif camera_type_used == "usb" and cap is not None: + cap.release() diff --git a/__pycache__/Cards.cpython-313.pyc b/__pycache__/Cards.cpython-313.pyc new file mode 100644 index 0000000..46cab8a Binary files /dev/null and b/__pycache__/Cards.cpython-313.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/_bootlocale.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/_bootlocale.cpython-39.pyc new file mode 100644 index 0000000..124bf0a Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/_bootlocale.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/_collections_abc.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/_collections_abc.cpython-39.pyc new file mode 100644 index 0000000..1195674 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/_collections_abc.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/_sitebuiltins.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/_sitebuiltins.cpython-39.pyc new file mode 100644 index 0000000..bdb6319 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/_sitebuiltins.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/_weakrefset.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/_weakrefset.cpython-39.pyc new file mode 100644 index 0000000..b87cef8 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/_weakrefset.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/abc.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/abc.cpython-39.pyc new file mode 100644 index 0000000..edb45af Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/abc.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/codecs.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/codecs.cpython-39.pyc new file mode 100644 index 0000000..942937a Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/codecs.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/collections/__init__.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/collections/__init__.cpython-39.pyc new file mode 100644 index 0000000..18eb394 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/collections/__init__.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/collections/abc.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/collections/abc.cpython-39.pyc new file mode 100644 index 0000000..8e81176 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/collections/abc.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/contextlib.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/contextlib.cpython-39.pyc new file mode 100644 index 0000000..1a51552 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/contextlib.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/copyreg.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/copyreg.cpython-39.pyc new file mode 100644 index 0000000..02ccfe0 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/copyreg.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/encodings/__init__.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/encodings/__init__.cpython-39.pyc new file mode 100644 index 0000000..f5f924a Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/encodings/__init__.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/encodings/aliases.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/encodings/aliases.cpython-39.pyc new file mode 100644 index 0000000..bb346e0 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/encodings/aliases.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/encodings/cp437.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/encodings/cp437.cpython-39.pyc new file mode 100644 index 0000000..9fe9cf1 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/encodings/cp437.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/encodings/latin_1.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/encodings/latin_1.cpython-39.pyc new file mode 100644 index 0000000..2217490 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/encodings/latin_1.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/encodings/utf_8.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/encodings/utf_8.cpython-39.pyc new file mode 100644 index 0000000..1217a10 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/encodings/utf_8.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/enum.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/enum.cpython-39.pyc new file mode 100644 index 0000000..77c1f77 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/enum.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/functools.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/functools.cpython-39.pyc new file mode 100644 index 0000000..3dbdcad Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/functools.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/genericpath.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/genericpath.cpython-39.pyc new file mode 100644 index 0000000..4aeb730 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/genericpath.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/heapq.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/heapq.cpython-39.pyc new file mode 100644 index 0000000..2750b85 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/heapq.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/importlib/__init__.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/importlib/__init__.cpython-39.pyc new file mode 100644 index 0000000..5bb3a7a Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/importlib/__init__.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/importlib/abc.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/importlib/abc.cpython-39.pyc new file mode 100644 index 0000000..9b728de Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/importlib/abc.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/importlib/machinery.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/importlib/machinery.cpython-39.pyc new file mode 100644 index 0000000..a4cde7a Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/importlib/machinery.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/importlib/util.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/importlib/util.cpython-39.pyc new file mode 100644 index 0000000..ccec6c1 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/importlib/util.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/io.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/io.cpython-39.pyc new file mode 100644 index 0000000..d2c7713 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/io.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/keyword.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/keyword.cpython-39.pyc new file mode 100644 index 0000000..1473749 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/keyword.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/linecache.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/linecache.cpython-39.pyc new file mode 100644 index 0000000..0030cb3 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/linecache.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/operator.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/operator.cpython-39.pyc new file mode 100644 index 0000000..3e73f35 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/operator.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/os.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/os.cpython-39.pyc new file mode 100644 index 0000000..f1dab36 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/os.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/pkgutil.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/pkgutil.cpython-39.pyc new file mode 100644 index 0000000..a8cb9cc Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/pkgutil.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/posixpath.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/posixpath.cpython-39.pyc new file mode 100644 index 0000000..7068e10 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/posixpath.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/py_compile.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/py_compile.cpython-39.pyc new file mode 100644 index 0000000..ae7578a Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/py_compile.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/re.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/re.cpython-39.pyc new file mode 100644 index 0000000..684a8ca Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/re.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/reprlib.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/reprlib.cpython-39.pyc new file mode 100644 index 0000000..a81bff8 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/reprlib.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/runpy.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/runpy.cpython-39.pyc new file mode 100644 index 0000000..201f0fc Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/runpy.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/site.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/site.cpython-39.pyc new file mode 100644 index 0000000..322971e Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/site.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/sre_compile.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/sre_compile.cpython-39.pyc new file mode 100644 index 0000000..6f49809 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/sre_compile.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/sre_constants.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/sre_constants.cpython-39.pyc new file mode 100644 index 0000000..b5b8866 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/sre_constants.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/sre_parse.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/sre_parse.cpython-39.pyc new file mode 100644 index 0000000..7f8ab08 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/sre_parse.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/stat.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/stat.cpython-39.pyc new file mode 100644 index 0000000..1f0dd68 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/stat.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/token.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/token.cpython-39.pyc new file mode 100644 index 0000000..0032ad2 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/token.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/tokenize.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/tokenize.cpython-39.pyc new file mode 100644 index 0000000..4e7e966 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/tokenize.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/traceback.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/traceback.cpython-39.pyc new file mode 100644 index 0000000..d1d5125 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/traceback.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/types.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/types.cpython-39.pyc new file mode 100644 index 0000000..d2911a8 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/types.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/typing.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/typing.cpython-39.pyc new file mode 100644 index 0000000..c6bcfd6 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/typing.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/warnings.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/warnings.cpython-39.pyc new file mode 100644 index 0000000..2e09c43 Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/warnings.cpython-39.pyc differ diff --git a/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/weakref.cpython-39.pyc b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/weakref.cpython-39.pyc new file mode 100644 index 0000000..08ca04d Binary files /dev/null and b/__pycache__/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/weakref.cpython-39.pyc differ diff --git a/__pycache__/Users/nyanprakash/Desktop/Behind/FractalBootcamp/OpenCV-Playing-Card-Detector/CardDetector.cpython-39.pyc b/__pycache__/Users/nyanprakash/Desktop/Behind/FractalBootcamp/OpenCV-Playing-Card-Detector/CardDetector.cpython-39.pyc new file mode 100644 index 0000000..b312d82 Binary files /dev/null and b/__pycache__/Users/nyanprakash/Desktop/Behind/FractalBootcamp/OpenCV-Playing-Card-Detector/CardDetector.cpython-39.pyc differ diff --git a/__pycache__/VideoStream.cpython-313.pyc b/__pycache__/VideoStream.cpython-313.pyc new file mode 100644 index 0000000..7e3f4e3 Binary files /dev/null and b/__pycache__/VideoStream.cpython-313.pyc differ