[PYTHON] Grattage écologique grâce au traitement d'image

A continué Essayez de créer un logiciel de capture aussi précis que possible avec python (2) https://qiita.com/akaiteto/items/56bfd8d764d42b9ff508 Essayez de créer un logiciel de capture aussi précis que possible avec python (1) https://qiita.com/akaiteto/items/b2119260d732bb189c87

introduction

J'en ai assez de la capture d'écran et audio, alors je vais faire une autre partie. Le processus suivant est un processus approximatif actuellement envisagé.

    1. Exécution de la commande à l'heure spécifiée -> 2. Démarrez le navigateur avec l'URL prédéfinie
    1. Capturez les captures d'écran et l'audio du navigateur
  1. Sortie vidéo en fusionnant les captures d'écran du navigateur et l'audio

Cette fois, je ferai la deuxième partie. Je souhaite soutenir de manière flexible toute structure du site Web Le but est de faire fonctionner le navigateur avec la détection d'images opencv sans analyser le HTML.

J'ai ajouté la raison de la modernisation, mais ... Puisque l'écran est pressé, je veux juste traiter l'image.

spécification

1. 1. Transition vers la destination de l'URL
2. Appuyez sur le bouton de lecture
3. 3. Détecter la zone de la carte météo
4. Enregistrer la zone détectée

Imaginez un site qui propose une vidéo d'une carte météo. L'ensemble du processus est supposé comme ci-dessus. Cette fois, ce sera probablement 1-3.

Environnement d'exécution

OS : windows10 ver: python3.7 web: chrome

1. 1. Transition vers la destination de l'URL

Selenium

Utilisons Selenium, une bibliothèque qui vous permet de faire fonctionner le navigateur à partir de commandes. https://qiita.com/hanzawak/items/2ab4d2a333d6be6ac760 https://rabbitfoot.xyz/selenium-chrome-profile/

En passant, j'utilise Chrome + pycharm, donc Lors de l'installation du pilote Web, installez-le dans l'environnement virtuel python.

#Installation dans un environnement virtuel
cd D:~ Omis ~\venv\Scripts
bat activate.bat
pip install chromedriver-binary==(La version de Chrome que vous utilisez)

Ouvrir le lien

Affichez le site de la carte météo. Enregistrez également l'image capturée du chrome ouvert pour les étapes ultérieures. Si vous utilisez la source ci-dessous, veuillez remplir la section (votre nom d'utilisateur). Pour plus de détails sur le chemin du profil, voir https://rabbitfoot.xyz/selenium-chrome-profile/


from selenium import webdriver
import chromedriver_binary  #Important
from selenium.webdriver.chrome.options import Options
import win32gui
import win32ui
import win32con
import numpy as np
import cv2

#Obtenez un écran en taille réelle
hnd = win32gui.GetDesktopWindow()
x0, y0, x1, y1 = win32gui.GetWindowRect(hnd)
fullscreen_width = x1 - x0
fullscreen_height = y1 - y0

#Taille du navigateur
browse_width=300
browse_height=fullscreen_height

#Lancer le navigateur
options = Options()
options.add_argument(r"--user-data-dir=C:\Users\(Ton nom d'utilisateur)\AppData\Local\Google\Chrome\User Data")
driver = webdriver.Chrome(chrome_options=options)
driver.get("url")
driver.set_window_size(browse_width,browse_height)
driver.set_window_position(0,0)

#Capture d'écran du navigateur
windc = win32gui.GetWindowDC(hnd)
srcdc = win32ui.CreateDCFromHandle(windc)
memdc = srcdc.CreateCompatibleDC()
bmp = win32ui.CreateBitmap()
bmp.CreateCompatibleBitmap(srcdc, browse_width, browse_height)
memdc.SelectObject(bmp)
memdc.BitBlt((0, 0), (browse_width, browse_height), srcdc, (0, 0), win32con.SRCCOPY)
bmp.SaveBitmapFile(memdc, 'PointDetect.bmp')


# driver.close()

Lors de l'ouverture d'un site nécessitant une connexion. Je ne veux pas écrire mon propre code brut de mot de passe parce que je ne l'aime pas physiologiquement.

** Si le site nécessite une connexion, connectez-vous à l'avance depuis Chrome **

user data directory is already in use, please specify a unique value for --user-data-dir argument, or don't use --user-data-dir

À propos, l'erreur ci-dessus se produit lors de l'exécution avec Chrome en cours d'exécution. Vous pouvez prendre des mesures, mais cette fois, il n'est pas nécessaire de démarrer plusieurs chromes, donc je ne le ferai pas.

2. Appuyez sur le bouton de lecture

Détectons le bouton par traitement d'image et cliquons dessus.

Comme aperçu du processus, Obtenez les coordonnées du bouton de lecture et cliquez sur les coordonnées dans la bibliothèque pyautogui. Le problème est d'obtenir les coordonnées du bouton de lecture. Les deux propositions suivantes doivent être examinées.

1. 1. Détectez la forme et obtenez les coordonnées
2. Trouver la même partie que l'image du bouton de lecture

1. 1. Détectez la forme et obtenez les coordonnées

1. 1. Détectez la forme et obtenez les coordonnées

Faisons-le facilement avec la fonction opencv.

DetectTriangle.py


import cv2
import numpy as np

def DetectTriangle(img, inputNm, outputNm):
    image_obj = cv2.imread(inputNm)
    img = cv2.adaptiveThreshold(img, 255, 1, 1, 11, 2)
    cv2.imwrite("PointDetect_threshold" + outputNm, img)

    contours, hierarchy = cv2.findContours(img,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_NONE)

    for cnt in contours:
        approx = cv2.approxPolyDP(cnt, 0.1 * cv2.arcLength(cnt, True), True)
        # approx = cv2.approxPolyDP(cnt, 0.07 * cv2.arcLength(cnt, True), True)  #Paramètres: affecte la précision
        # approx = cv2.approxPolyDP(cnt, .03 * cv2.arcLength(cnt, True), True)   #Paramètres: affecte la précision
        # approx = cv2.approxPolyDP(cnt, .009 * cv2.arcLength(cnt, True), True)  #Paramètres: affecte la précision

        if len(approx) == 3:
            print("triangle")
            cv2.drawContours(image_obj, [cnt], 0, (0, 0, 255), -1)
        elif len(approx) == 4:
            print("square")
            cv2.drawContours(image_obj, [cnt], 0, (0, 255, 0), -1)
        elif len(approx) == 8:
            print("circle")
            area = cv2.contourArea(cnt)
            (cx, cy), radius = cv2.minEnclosingCircle(cnt)
            circleArea = radius * radius * np.pi
            if circleArea == area:
                cv2.drawContours(image_obj, [cnt], 0, (255, 0, 0), -1)

    cv2.imwrite(outputNm, image_obj)

inputNm = 'PointDetect2.bmp'
srcImage = cv2.imread(inputNm)

gray = cv2.cvtColor(srcImage, cv2.COLOR_BGR2GRAY)
cv2.imwrite("PointDetect_gray.png ", gray)

kernel = np.ones((4, 4), np.uint8)
dilation = cv2.dilate(gray, kernel, iterations=1)
cv2.imwrite("PointDetect_dilation.png ", dilation)

blur = cv2.GaussianBlur(dilation, (5, 5), 0)
cv2.imwrite("PointDetect_blur.png ", dilation)

DetectTriangle(blur,inputNm,"result_blur.png ")         #Brouiller
DetectTriangle(gray,inputNm,"result_gray.png ")         #niveaux de gris
DetectTriangle(dilation,inputNm,"result_dilation.png ") #Gonfler (car le bouton de lecture est petit)

Le traitement que vous souhaitez effectuer est à peu près le suivant.

1. 1. Prétraitement
2. Traitement des seuils
3. 3. Extraction de contour

Je vais l'essayer avec la recherche d'images google sukusho. PointDetect2.jpg

Échelle de gris comme prétraitement ↓ (je fais diverses autres choses dans la source) PointDetect_gray.png

Traitement des seuils ↓ PointDetect_thresholdresult_dilation.png

Extraction de contour ↓ result_gray.png

Le rouge est un triangle, le vert est un carré et le bleu est un cercle. A partir des coordonnées du contour de la figure détecté ici C'est un calcul pour spécifier la position du bouton de lecture, qui est une forme de triangle.

・ ・ ・ ・ ・ ・

Alors, je suis allé sur le site météo. Le résultat n'était ... pas bon. J'ai essayé du flou, de l'expansion, du réglage des paramètres, etc., mais cela n'a pas fonctionné.

Le bouton de lecture a également été détecté, mais il y a trop de faux positifs. En premier lieu, il ne convient pas aux sites comportant de nombreuses figures triangulaires, Un autre inconvénient est que vous devrez peut-être ajuster les paramètres appropriés pour chaque site.

Il serait possible de détecter spécifiquement ce site météorologique, Je veux en faire un format qui puisse être fait sur différents sites, donc le plan 1 est rejeté.

2. Trouver la même partie que l'image du bouton de lecture

2. Trouver la même partie que l'image du bouton de lecture

Je vais essayer de le détecter par correspondance de modèle. C'est un processus pour détecter si la même chose qu'une petite image est contenue dans une autre image. Pour effectuer une correspondance de modèles, il est nécessaire d'indiquer à l'image du bouton de lecture que la réponse est correcte.

無題.png Donc, sous le flux ci-dessus, Ajoutez une phase appelée "Obtenir l'image du bouton de lecture (manuel)". Je voulais tout faire automatiquement, mais cela ne peut pas être aidé.

無題.png

Référence: https://shizenkarasuzon.hatenablog.com/entry/2020/03/23/005440

Imagewise Une petite fenêtre comme celle ci-dessus apparaîtra et l'utilisateur sélectionnera le bouton sur lequel il souhaite cliquer dans la fenêtre de sélection carrée (bleu clair). Enregistrez l'image pour le modèle correspondant à l'image de ... Le fonctionnement manuel n'est supposé que pour la première fois.

Correspondance de modèle

Je vais donc essayer la correspondance de modèles.

http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_imgproc/py_template_matching/py_template_matching.html

Quand je l'ai exécuté selon la source de l'échantillon openCV, cela a très bien fonctionné. Allons-y avec la politique de 2

3. 3. Détecter la zone de la carte météo

Je joue avec l'absdiff d'opencv (divers) Effectue une détection de mouvement sur les images avant et après avoir appuyé sur le bouton de lecture, Détecte la zone de changement.

Résumé

Je vais résumer les sources jusqu'à présent.

Comme prémisse, l'image de la pièce sur laquelle vous voulez cliquer, Supposons que vous ayez déjà une image du bouton de lecture ici. ↓ Découpez l'image comme ci-dessous PointDetect_patch.jpg

Avant d'exécuter, installez les bibliothèques suivantes.

pip install PyAutoGUI

Dans mon environnement, l'erreur suivante s'est produite et l'installation a échoué.

SyntaxError: (unicode error) 'utf-8' codec can't decode byte 0x93 in position 0: invalid start byte (sitecustomize.py, line 7)

https://qiita.com/hisakichi95/items/41002333efa8f6371d40 J'ai installé une ancienne version de PyMsgBox en faisant référence à.

Donc, la source suivante. Je ne l'organise pas

Detect.py


from selenium import webdriver
import chromedriver_binary  #Important
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
import win32gui
import win32ui
import win32con
import numpy as np
import cv2

def DetectMotion(ImgNm1,ImgNm2):
    img1 = cv2.imread(ImgNm1, 0)
    img2 = cv2.imread(ImgNm2, 0)

    img1 = img1.copy().astype("float")
    cv2.accumulateWeighted(img2, img1, 0.6)

    cv2.accumulateWeighted(img2, img1, 0.6)
    frameDelta = cv2.absdiff(img2, cv2.convertScaleAbs(img1))

    thresh = cv2.threshold(frameDelta, 3, 255, cv2.THRESH_BINARY)[1]
    contours, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    img2 = cv2.imread(TEMP_AFTER_SCREENSHOT)
    top_left_X = 9999999
    top_left_Y = 9999999
    bum_right_X = 0
    bum_right_Y = 0

    for i in range(0, len(contours)):
        if len(contours[i]) > 0:

            # remove small objects
            if cv2.contourArea(contours[i]) < 500:
                continue

            rect = contours[i]
            x, y, w, h = cv2.boundingRect(rect)
            pos_top = (x, y)
            pos_bum = (x + w, y + h)

            print(x, y, x + w, y + h)
            top_left_X = pos_top[0] if top_left_X > pos_top[0] else top_left_X
            top_left_Y = pos_top[1] if top_left_Y > pos_top[1] else top_left_Y
            bum_right_X = pos_bum[0] if bum_right_X < pos_bum[0] else bum_right_X
            bum_right_Y = pos_bum[1] if bum_right_Y < pos_bum[1] else bum_right_Y

    return (top_left_X, top_left_Y), (bum_right_X, bum_right_Y)

def DiffImage(img1,img2):
    im_diff = img1.astype(int) - img2.astype(int)
    im_diff_abs = np.abs(im_diff)
    return im_diff_abs.max()

def DetectBtn(bmp,memdc,CapFIleNm,PatchNm,scrollY):
    memdc.BitBlt((0, 0), (browse_width, browse_height), srcdc, (0, 0), win32con.SRCCOPY)
    bmp.SaveBitmapFile(memdc, CapFIleNm)
    #Obtenir les coordonnées du bouton
    img = cv2.imread(CapFIleNm, 0)

    img2 = img.copy()
    template = cv2.imread(PatchNm, 0)
    w, h = template.shape[::-1]

    meth = 'cv2.TM_CCOEFF_NORMED'
    img = img2.copy()
    method = eval(meth)

    res = cv2.matchTemplate(img, template, method)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)

    if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
        top_left = min_loc
    else:
        top_left = max_loc
    bottom_right = (top_left[0] + w, top_left[1] + h)

    cv2.rectangle(img, top_left, bottom_right, 255, 2)
    range = ((bottom_right[0] - top_left[0]) / 2, (bottom_right[1] - top_left[1]) / 2)
    btn_center = (int(top_left[0] + range[0]), int(top_left[1] + range[1]))
    print("Coordonnées en haut à gauche du bouton", top_left)
    print("Coordonnées en haut à droite du bouton", bottom_right)
    print("Coordonnées du centre du bouton", btn_center)

    #Découpez la partie détectée
    img1 = img[top_left[1]: bottom_right[1], top_left[0]: bottom_right[0]]

    if DiffImage(template,img1) > 180:
        #Trop différent de l'image du bouton-> False
        cv2.imwrite("Detect_Fail" + str(scrollY) + ".jpg ", img1)
        print("btn not exist")
        return False,(0,0)
    else:
        #Succès-> True
        cv2.imwrite("Detect_Success" + str(scrollY) + ".jpg ", img1)
        print("btn exist")
        return True,btn_center

TEMP_BEFORE_SCREENSHOT = 'PointDetect_before.bmp'
TEMP_AFTER_SCREENSHOT = 'PointDetect_after.bmp'
TEMP_PATCH = 'PointDetect_patch.jpg'

#Obtenez un écran en taille réelle
hnd = win32gui.GetDesktopWindow()
x0, y0, x1, y1 = win32gui.GetWindowRect(hnd)
fullscreen_width = x1 - x0
fullscreen_height = y1 - y0

#Taille du navigateur
# browse_width=fullscreen_width
# browse_height=fullscreen_height
browse_width=1920
browse_height=1080

#Lancer le navigateur
options = Options()
# options.add_argument(r"--user-data-dir=C:\Users\Ton nom d'utilisateur\AppData\Local\Google\Chrome\User Data")
driver = webdriver.Chrome(chrome_options=options)
driver.get("https://")
driver.set_window_size(browse_width,browse_height)
driver.set_window_position(0,0)

#En attente du comportement du navigateur
import time
time.sleep(3)

#Préparation Sukusho
windc = win32gui.GetWindowDC(hnd)
srcdc = win32ui.CreateDCFromHandle(windc)
memdc = srcdc.CreateCompatibleDC()
bmp = win32ui.CreateBitmap()
bmp.CreateCompatibleBitmap(srcdc, browse_width, browse_height)
memdc.SelectObject(bmp)

#Faites défiler jusqu'à ce que vous trouviez le bouton
Detectflg=False
isScrolButton=False
scrollY = 0
while Detectflg==False:
    scrollY += int(fullscreen_height/4)
    #Obtenez une capture avant et après le défilement
    memdc.BitBlt((0, 0), (browse_width, browse_height), srcdc, (0, 0), win32con.SRCCOPY)
    bmp.SaveBitmapFile(memdc, TEMP_BEFORE_SCREENSHOT)
    driver.execute_script("window.scrollTo(0, "+ str(scrollY) +")")
    time.sleep(5)
    memdc.BitBlt((0, 0), (browse_width, browse_height), srcdc, (0, 0), win32con.SRCCOPY)
    bmp.SaveBitmapFile(memdc, TEMP_AFTER_SCREENSHOT)

    img1 = cv2.imread(TEMP_BEFORE_SCREENSHOT, 0)
    img2 = cv2.imread(TEMP_AFTER_SCREENSHOT, 0)

    diff = DiffImage(img1,img2)
    if diff < 100:
        #L'écran ne change pas même si vous faites défiler->J'ai échoué parce que je suis arrivé au bas du rouleau
        print("scrollbutton")
        flg=True,btn_pos
        isScrolButton=True

    flg,btn_pos = DetectBtn(bmp,memdc,TEMP_AFTER_SCREENSHOT,TEMP_PATCH,scrollY)
    Detectflg = flg

#Cliquez sur les coordonnées du bouton
if isScrolButton:
    print("Bouton introuvable")
    exit()

#Régénération
import pyautogui
pyautogui.click(btn_pos[0],btn_pos[1])

#Enregistrer avant le changement
memdc.BitBlt((0, 0), (browse_width, browse_height), srcdc, (0, 0), win32con.SRCCOPY)
bmp.SaveBitmapFile(memdc, TEMP_BEFORE_SCREENSHOT)

#Attendez que l'écran change
time.sleep(5)

#Enregistrer après modification
memdc.BitBlt((0, 0), (browse_width, browse_height), srcdc, (0, 0), win32con.SRCCOPY)
bmp.SaveBitmapFile(memdc, TEMP_AFTER_SCREENSHOT)

#Détection de mouvement
top_left,bottom_right = DetectMotion(TEMP_BEFORE_SCREENSHOT,TEMP_AFTER_SCREENSHOT)
img = cv2.imread(TEMP_AFTER_SCREENSHOT)
img1 = img[top_left[1]: bottom_right[1], top_left[0]: bottom_right[0]]
cv2.imwrite("MotionArea.jpg ", img1)

# driver.close()

Fonction de défilement pour trouver un bouton et Nous avons ajouté un processus pour prendre la différence de l'image pour une fausse détection lorsque le bouton est détecté.

La prochaine fois, j'aimerais le combiner avec la fonction d'enregistrement que j'ai créée la dernière fois. Eh bien

Recommended Posts

Grattage écologique grâce au traitement d'image
[Traitement d'image] Postérisation
traitement d'image python
Traitement d'image 100 coups ①
Scraping à l'aide de Python
Traitement d'image avec MyHDL
Grattage de l'image du blog de Hinatazaka
Lire le traitement d'image numérique
Segmentation d'image à l'aide de U-net
Traitement d'image avec Python
Traitement d'image avec PIL
Scraping à l'aide de Python 3.5 async / await
Traitement d'image avec Python (partie 2)
opencv-python Introduction au traitement d'image
Traitement d'image avec PIL (Pillow)
Essayez d'utiliser l'image Docker de Jupyter
Traitement d'image numérique (filtrage spatial)
100 coups sur le traitement d'image !! (011-020) Début de partie
Scraping à l'aide de la syntaxe Python 3.5 Async
Traitement d'image avec Python (partie 1)
Prédiction d'images dans le cloud à l'aide de convLSTM
Collection d'images avec scraping Web
Web scraping avec Selenium (Python)
Traitement d'image avec Python (3)
Traitement d'image par python (Pillow)
Collection de traitement d'image en Python
Web scraping à l'aide d'AWS lambda
Implémenter le traitement de l'inversion à l'aide de BitBoard
Traitement de l'expansion et de la contraction de l'image
Utilisation du mode Python dans le traitement
[Python] Traitement d'image avec scicit-image
Comprendre la fonction de convolution en utilisant le traitement d'image comme exemple
[Traitement d'image] Poo-san est nu par détection de bord en utilisant Python et OpenCV!