Visão computacional - controle de detecção de casco convexo para comer jogo de moedas de ouro

Coma moedas de ouro ( construção básica )

(Fonte de construção do jogo CSDN) 

O arquivo de configuração cfg armazena os caminhos de fontes, imagens e áudio exigidos pelo jogo

 

A foto abaixo é uma foto de pessoas correndo no arquivo de imagem de recursos

Os principais componentes do jogo:

①O vilão que come moedas de ouro é definido como a classe do herói

Configuração principal: 1) Especifique a posição inicial e a orientação inicial da tela do herói

                  2) Defina a função de movimento para descrever o movimento do vilão através do movimento da posição e a mudança da ação

    def move(self, screensize, direction):
        assert direction in ['left', 'right']
        if direction != self.diretion:
            self.images = self.images_left.copy() if direction == 'left' else self.images_right.copy()
            self.image = self.images[0]
            self.diretion = direction
            self.switch_frame_count = 0
        self.switch_frame_count += 1
        if self.switch_frame_count % self.switch_frame_freq == 0:
            self.switch_frame_count = 0
            self.frame_index = (self.frame_index + 1) % len(self.images)
            self.image = self.images[self.frame_index]
        if direction == 'left':
            self.rect.left = max(self.rect.left-self.speed, 0)
        else:
            self.rect.left = min(self.rect.left+self.speed, screensize[0])
  1. 3) Desenhe a mudança do vilão em segundo plano

②Moedas de ouro em queda são definidas como classe alimentar

Configuração principal: 1) Especifique a posição inicial da comida, dividida em dois tipos de moedas de ouro no total, e diferentes moedas de ouro têm pontuações diferentes

                  2) Formule uma função de atualização para fazê-la cair a uma certa velocidade. Quando as coisas não chegarem ao fundo, atualize continuamente a posição da moeda de ouro.

③Configuração da tela do jogo

É principalmente para construir a interface do jogo e usar pygame.time para manter a interface no loop principal.

④Como o jogo main.py funciona?

Realização principal: 1) Interface, música, inicialização de fonte

2) Obtenha uma entidade de herói e vários grupos de entidades de alimentos

3) Gere alimentos aleatoriamente e atualize continuamente a posição dos alimentos na tela (movimento vertical)

4) Detecção de botão, se <--- for detectado, o vilão se moverá um passo para a esquerda e atualizará a posição.

     

   for event in pygame.event.get():

            if event.type == pygame.QUIT:

                pygame.quit()

                sys.exit()

        key_pressed = pygame.key.get_pressed()

        if key_pressed[pygame.K_a] or key_pressed[pygame.K_LEFT]:

            hero.move((600,400), 'left')

        if key_pressed[pygame.K_d] or key_pressed[pygame.K_RIGHT]:

            hero.move((600,400), 'right')
  1. 5) Se for detectado que o herói vilão colide com a comida, será pontuado.

     

   for food in food_sprites_group:

            if pygame.sprite.collide_mask(food, hero):

                game_sounds['get'].play()

                food_sprites_group.remove(food)

                score += food.score

                if score > highest_score: highest_score = score
  1. 6) Atualize a tela com base nas informações de pontuação

Detecção de tambor:

2.2.1 Jogo 1 (comer moedas de ouro) resumo do algoritmo (detecção de casco convexo baseado em opencv)

Com base na detecção de casco convexo do dedo, a imagem precisa ser capturada no início e precisamos executar as seguintes operações na imagem capturada

① Realize filtragem bilateral

ret, frame = camera.read()

frame = cv2.bilateralFilter(frame, 5, 50, 100) # filtragem bilateral

frame = cv2.flip(frame,1) # Flip 0: Virar ao longo do eixo X (virar vertical) Maior que 0: Virar ao longo do eixo Y #Flip ( virar horizontal) Menor que 0: Virar primeiro ao longo do eixo X, depois inverta ao longo do eixo Y, etc. O valor é girado em 180°    

cv2.imshow('origem',quadro)

A filtragem bilateral é baseada na filtragem ponderada média ou comum (como a filtragem gaussiana) e executa a suavização ponderada na imagem por meio de dois pesos de peso de distância e peso de cor, que podem remover ruídos e proteger bordas.

Essa característica da filtragem bilateral se dá principalmente porque ele considera tanto a distância geométrica quanto a distância de cor entre os pixels ao suavizar o filtro. Na área onde a imagem muda suavemente, os valores de brilho dos pixels na vizinhança não são muito diferentes, e o filtro bilateral é transformado em um filtro gaussiano passa-baixo; valor de brilho original. Portanto, o filtro bilateral não apenas suaviza a imagem filtrada, mas também mantém as bordas da imagem .

Esta é a maior diferença entre ele e a filtragem gaussiana e outros métodos. É um processamento de compromisso que combina a proximidade espacial da imagem e a semelhança dos valores de pixel, enquanto considera a informação espacial e a semelhança de cinza para atingir o objetivo de preservação de borda e redução de ruído . É simples, não iterativo e local.

Como o que precisamos detectar desta vez é a pele, primeiro definimos um limite de cor de pele e, ao mesmo tempo, executamos um processamento de limite na imagem.

inferior = np.array([0, 48, 80], dtype = "uint8")

upper = np.array([20, 255, 255], dtype = "uint8")

#Defina o limite da cor da pele, que pode ser ajustado de acordo com a luz ou a cor da minha pele

skinRegionHSV = cv.inRange(hsvim, inferior, superior)

borrado = cv.blur(skinRegionHSV, (2,2))

ret,thresh = cv.threshold(desfocado,0,255,cv.THRESH_BINARY)

cv.imshow("debulhar", debulhar)

②Escala de cinza da imagem, filtro médio (remover ruído, facilitar o processamento da imagem)

③Detecção da cor da pele

Após a detecção, obteremos uma imagem binária apenas com as mãos. Esse efeito pode ser alcançado conforme o esperado, mas, na verdade, é difícil remover vários ruídos ao redor e existem muitos ruídos na realidade.

 

Após a obtenção da imagem binarizada, o próximo passo é detectar um casco convexo do dedo, o primeiro passo é desenhar o contorno do dedo.

# Desenho de contorno

contornos, hierarquia = cv.findContours (thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
contornos = max(contornos, chave=lambda x: cv.contourArea(x))
cv.drawContours (img, [contornos], -1, ( 255,255,0), 2)
cv.imshow("contornos", img)

 

O próximo passo é usar a função de detecção de casco convexo convexHull que vem com opencv para detectar o número de dedos Aqui, primeiro introduza brevemente o conceito de casco convexo.

Convex Hull é um conceito em geometria computacional (gráficos). No espaço euclidiano bidimensional, a casca convexa pode ser imaginada como um elástico que apenas envolve todos os pontos. Em termos vagos, dado um ponto definido em um plano bidimensional, um casco convexo é um polígono convexo formado pela conexão dos pontos mais externos, que podem conter todos os pontos no conjunto de pontos.

Exemplo: Supondo que existam 13 pontos p0~p12 no plano, um polígono é feito através de alguns pontos para que este polígono possa "envolver" todos os pontos. Quando este polígono é convexo, chamamos de "casco convexo". Como mostrado abaixo: 

casco = cv.convexHull(contornos)
cv.drawContours (img, [casco], -1, (0, 255, 255), 2)
cv.imshow("casco", img)

Qualquer depressão em um objeto é chamada de defeito convexo, e a função cv.convexityDefect() pode nos ajudar a encontrar defeitos convexos. Ele retornará um array onde cada linha contém os valores [ponto inicial, ponto final, ponto mais distante, distância aproximada ao ponto mais distante].

hull = cv.convexHull (contours, returnPoints=False)
defeitos = cv.convexityDefects (contours, hull)

 

A principal tarefa é contar o número de dedos, porque através da detecção de casco convexo e detecção de defeito convexo, posso determinar a posição da ponta do dedo e a posição da depressão de transferência entre os dedos, se os três pontos adjacentes puderem formar um triângulo, e o ângulo incluído for inferior a 90 graus, então pensamos que há dois aqui.

Então nosso problema agora é, dados os três lados do triângulo, como podemos julgar se o ângulo diagonal é menor que 90 graus? Através do conhecimento matemático, podemos usar a lei dos cossenos para encontrar o número de ângulos diagonais de c. 

 

 

Usando essas duas fórmulas, podemos obter o grau de θ. Se for menor que 90 graus, podemos considerá-lo como tendo dois dedos. Se houver 4 θ diferentes, podemos considerá-lo como tendo 5 dedos.

Depois que os dedos são detectados, implementamos principalmente o controle por meio da classe herói (vilão do esporte) que come moedas de ouro, definimos como uma variável global e executamos os dois threads do jogo e a detecção de dedos ao mesmo tempo. de dedos é detectado, passe A função de movimento integrada do herói controla a direção do movimento do vilão. Existem apenas duas direções aqui: quando o dedo detectado é 1 ou 2, o vilão se move para a esquerda, caso contrário, o vilão se move para a direita.

herói global

if(cnt<=2):

hero.move(cfg.SCREENSIZE, 'esquerda')

outro:

hero.move(cfg.SCREENSIZE, 'direita')

Efeito de demonstração:

 

 Código (programa principal, excluindo arquivos de configuração):


# coding: utf-8

# In[1]:


import threading
import time
import os

import cv2
import numpy as np
import copy
import time
from modules import *


import os
import cfg
import cv2 as cv
import numpy as np
import sys
import pygame
import random
from modules import *
import cv2
cnt1=1
def main_gesture(): 

    cap_region_x_begin = 0.3 # 起点/总宽度
    cap_region_y_end = 0.8
    threshold = 60 # 二值化阈值
    blurValue = 41 # 高斯模糊参数
    bgSubThreshold = 50
    learningRate = 0
 
    
    isBgCaptured = 0 
    triggerSwitch = False 
    camera = cv2.VideoCapture(1)  
    camera.set(10, 200) 
    t1=time.time()
    i=1
    while camera.isOpened():
        ret, frame = camera.read()
        frame = cv2.bilateralFilter(frame, 5, 50, 100) # 双边滤波
        frame = cv2.flip(frame,1) # 翻转 0:沿X轴翻转(垂直翻转)  大于0:沿Y轴翻转(水平翻转)  小于0:先沿X轴翻转,再沿Y轴翻转,等价于旋转180°
        cv2.imshow('orig',frame) 
        if isBgCaptured == 1: 
            img=frame
            img = img[0:int(cap_region_y_end * frame.shape[0]),int(cap_region_x_begin * frame.shape[1]):frame.shape[1]] # 剪切右上角矩形框区域
            hsvim = cv.cvtColor(img, cv.COLOR_BGR2HSV)
            lower = np.array([0, 48, 80], dtype = "uint8")
            upper = np.array([20, 255, 255], dtype = "uint8")
            skinRegionHSV = cv.inRange(hsvim, lower, upper)
            blurred = cv.blur(skinRegionHSV, (2,2))
            ret,thresh = cv.threshold(blurred,0,255,cv.THRESH_BINARY)
            cv.imshow("thresh", thresh)
            _,contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
            
            try:
                contours = max(contours, key=lambda x: cv.contourArea(x))
            except ValueError as e:
                cv.imshow('final_result',img)
                continue
                
     
            cv.drawContours(img, [contours], -1, (255,255,0), 2)
          
            hull = cv.convexHull(contours)
            cv.drawContours(img, [hull], -1, (0, 255, 255), 2)

            hull = cv.convexHull(contours, returnPoints=False)
            defects = cv.convexityDefects(contours, hull)
            if defects is not None:
                cnt = 0
                for i in range(defects.shape[0]): # calculate the angle
                    s, e, f, d = defects[i][0]
                    start = tuple(contours[s][0])
                    end = tuple(contours[e][0])
                    far = tuple(contours[f][0])
                    a = np.sqrt((end[0] - start[0]) ** 2 + (end[1] - start[1]) ** 2)
                    b = np.sqrt((far[0] - start[0]) ** 2 + (far[1] - start[1]) ** 2)
                    c = np.sqrt((end[0] - far[0]) ** 2 + (end[1] - far[1]) ** 2)
                    angle = np.arccos((b ** 2 + c ** 2 - a ** 2) / (2 * b * c)) # cosine theorem
                    if angle <= np.pi / 2: # angle less than 90 degree, treat as fingers
                        cnt += 1
                        cv.circle(img, far, 4, [0, 0, 255], -1)
                        
                t2=time.time()
                if cnt > 0:
                    cnt = cnt
                    if(t2-t1<=2):
                        cnt=cnt1   
                    else:
                        cnt1=cnt
                        t1=t2
                    if(cnt<=2):
                        ttt="<<-"
                    else:
                        ttt="->>"
                    cv.putText(img, ttt, (200, 80), cv.FONT_HERSHEY_SIMPLEX,3, (0,0, 255) , 1, cv.LINE_AA)     
            
            cv.imshow('final_result',img)
            global hero
            if(cnt<=2):
                hero.move(cfg.SCREENSIZE, 'left')
            else:
                hero.move(cfg.SCREENSIZE, 'right')
            
                
        k = cv2.waitKey(10)
        if k == ord('b'): 
            bgModel = cv2.createBackgroundSubtractorMOG2(0, bgSubThreshold)
            isBgCaptured = 1
            print('!!!Background Captured!!!')
        elif k == ord('r'): # 按下'r'会重置背景
            bgModel = None
            triggerSwitch = False
            isBgCaptured = 0
            print('!!!Reset BackGround!!!')
        elif k == ord('n'):
            triggerSwitch = True
            print('!!!Trigger On!!!')
        elif k==ord('q'):
            camera.release()
            break
    
          

import os
import cfg
import sys
import pygame
import random
from modules import *


'''游戏初始化'''
def initGame():
    # 初始化pygame, 设置展示窗口
    pygame.init()
    screen = pygame.display.set_mode((600,400))
    pygame.display.set_caption('catch coins —— 吃金币游戏')
    # 加载必要的游戏素材
    game_images = {}
    for key, value in cfg.IMAGE_PATHS.items():
        if isinstance(value, list):
            images = []
            for item in value: images.append(pygame.image.load(item))
            game_images[key] = images
        else:
            game_images[key] = pygame.image.load(value)
    game_sounds = {}
    for key, value in cfg.AUDIO_PATHS.items():
        if key == 'bgm': continue
        game_sounds[key] = pygame.mixer.Sound(value)
    # 返回初始化数据
    return screen, game_images, game_sounds


'''主函数'''
def main():
    # 初始化
    screen, game_images, game_sounds = initGame()
    # 播放背景音乐
    pygame.mixer.music.load(cfg.AUDIO_PATHS['bgm'])
    pygame.mixer.music.play(-1, 0.0)
    # 字体加载
    font = pygame.font.Font(cfg.FONT_PATH, 40)
    # 定义hero
    global hero
    hero = Hero(game_images['hero'], position=(375, 320))
    # 定义食物组
    food_sprites_group = pygame.sprite.Group()
    generate_food_freq = random.randint(10, 20)
    generate_food_count = 0
    # 当前分数/历史最高分
    score = 0
    highest_score = 0 if not os.path.exists(cfg.HIGHEST_SCORE_RECORD_FILEPATH) else int(open(cfg.HIGHEST_SCORE_RECORD_FILEPATH).read())
    # 游戏主循环
    clock = pygame.time.Clock()
    while True:
        # --填充背景
        screen.fill(0)
        screen.blit(game_images['background'], (0, 0))
        cfg.SCREENSIZE=(600,400)
        # --倒计时信息
        countdown_text = 'Count down: ' + str((90000 - pygame.time.get_ticks()) // 60000) + ":" + str((90000 - pygame.time.get_ticks()) // 1000 % 60).zfill(2)
        countdown_text = font.render(countdown_text, True, (0, 0, 0))
        countdown_rect = countdown_text.get_rect()
        countdown_rect.topright = [cfg.SCREENSIZE[0]-30, 5]
        screen.blit(countdown_text, countdown_rect)
        # --按键检测
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
        key_pressed = pygame.key.get_pressed()
        if key_pressed[pygame.K_a] or key_pressed[pygame.K_LEFT]:
            hero.move((600,400), 'left')
        if key_pressed[pygame.K_d] or key_pressed[pygame.K_RIGHT]:
            hero.move((600,400), 'right')
        # --随机生成食物
        generate_food_count += 1
        if generate_food_count > generate_food_freq:
            generate_food_freq = random.randint(10, 20)
            generate_food_count = 0
            food = Food(game_images, random.choice(['gold',] * 10 + ['apple']), cfg.SCREENSIZE)
            food_sprites_group.add(food)
        # --更新食物
        for food in food_sprites_group:
            if food.update(): food_sprites_group.remove(food)
        # --碰撞检测
        for food in food_sprites_group:
            if pygame.sprite.collide_mask(food, hero):
                game_sounds['get'].play()
                food_sprites_group.remove(food)
                score += food.score
                if score > highest_score: highest_score = score
        # --画hero
        hero.draw(screen)
        # --画食物
        food_sprites_group.draw(screen)
        # --显示得分
        score_text = f'Score: {score}, Highest: {highest_score}'
        score_text = font.render(score_text, True, (0, 0, 0))
        score_rect = score_text.get_rect()
        score_rect.topleft = [5, 5]
        screen.blit(score_text, score_rect)
        # --判断游戏是否结束
        if pygame.time.get_ticks() >= 90000:
            break
        # --更新屏幕
        pygame.display.flip()
        clock.tick(cfg.FPS)
    # 游戏结束, 记录最高分并显示游戏结束画面
    fp = open(cfg.HIGHEST_SCORE_RECORD_FILEPATH, 'w')
    fp.write(str(highest_score))
    fp.close()
    return showEndGameInterface(screen, cfg, score, highest_score)

import threading
import time
import os
from multiprocessing import Process,Value
#线程条件变量
threading.Thread(target=main).start()  
threading.Thread(target=main_gesture).start() 

Acho que você gosta

Origin blog.csdn.net/cangzhexingxing/article/details/124124094
Recomendado
Clasificación