I am trying to make a script for fishing minigame, there is a post with a fishing script for albion online, the problem is that in this game the pointer is very thin and there are many different textures and colors, to the simple grayscale matching works good on one part of the slider (for example on the water texture), sometimes work on others(trees texture) and does not work on the third (ex. sky). If I change to lower threshold it activates often without matching.
import numpy as np
import cv2
from mss.windows import MSS as mss
from PIL import Image
import time
import pyautogui as pg
import cv2
import mss
import numpy
template = cv2.imread("perfect3.png", cv2.IMREAD_GRAYSCALE)
w, h = template.shape[::-1]
fgbg = cv2.createBackgroundSubtractorMOG2(
history=10,
varThreshold=2,
detectShadows=False)
with mss.mss() as sct:
monitor = {"top": 344, "left": 4419, "width": 150, "height": 666}
while "Screen capturing":
last_time = time.time()
img = numpy.array(sct.grab(monitor))
gray_frame = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
res = cv2.matchTemplate(gray_frame, template, cv2.TM_CCOEFF_NORMED)
loc = np.where(res >= 0.85)
for pt in zip(*loc[::-1]):
cv2.rectangle(img, pt, (pt[0] + w, pt[1] + h), (0, 255, 0), 3)
print('click')
cv2.imshow("OpenCV/Numpy normal", img)
key = cv2.waitKey(1)
if cv2.waitKey(25) & 0xFF == ord("q"):
cv2.destroyAllWindows()
break
Tried it also with edge detection cv2.canny without luck.
The point is to click the button when the fish is in the smallest green field. The field appears in random parts of a slider.
Any ideas?
================
UPDATE
Tried color matching as Furas said
# import the necessary packages
from collections import deque
from imutils.video import VideoStream
import numpy as np
import argparse
import cv2
import imutils
import time
from mss.linux import MSS as mss
from PIL import Image
import mss
import numpy
import pyautogui
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-v", "--video",
help="path to the (optional) video file")
ap.add_argument("-b", "--buffer", type=int, default=64,
help="max buffer size")
args = vars(ap.parse_args())
# define the lower and upper boundaries of the "green"
# ball in the HSV color space, then initialize the
# list of tracked points
greenLower = (42, 84, 211)
greenUpper = (69, 130, 255)
blueLower = (88, 76, 255)
blueUpper = (151, 76, 255)
pts = deque(maxlen=args["buffer"])
# grab video from screen(monitor area)
with mss.mss() as sct:
monitor = {"top": 325, "left": 4423, "width": 136, "height": 662}
while "Screen capturing":
#last_time = time.time()
#vs = numpy.array(sct.grab(monitor))
#print("fps: {}".format(1 / (time.time() - last_time)))
vs = sct.grab(monitor)
# grab the current frame
#frame = vs
frame = np.array(vs)
# resize the frame, blur it, and convert it to the HSV
# color space
blurred = cv2.GaussianBlur(frame, (11, 11), 0)
hsv = cv2.cvtColor(blurred, cv2.COLOR_BGR2HSV)
# construct a mask for the color "green", then perform
# a series of dilations and erosions to remove any small
# blobs left in the mask
mask = cv2.inRange(hsv, greenLower, greenUpper)
mask = cv2.erode(mask, None, iterations=2)
mask = cv2.dilate(mask, None, iterations=2)
mask2 = cv2.inRange(hsv, blueLower, blueUpper)
mask2 = cv2.erode(mask2, None, iterations=2)
mask2 = cv2.dilate(mask2, None, iterations=2)
# find contours in the mask and initialize the current
# (x, y) center of the ball
cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
center = None
cnts2 = cv2.findContours(mask2.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts2 = imutils.grab_contours(cnts2)
center2 = None
# only proceed if at least one contour was found
if len(cnts) > 0:
# find the largest contour in the mask, then use
# it to compute the minimum enclosing rectangle and
# centroid
c = max(cnts, key=cv2.contourArea)
(x, y, w, h) = cv2.boundingRect(c)
M = cv2.moments(c)
center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))
c2 = max(cnts2, key=cv2.contourArea)
(x2, y2, w2, h2) = cv2.boundingRect(c2)
M2 = cv2.moments(c2)
center2 = (int(M2["m10"] / M2["m00"]), int(M2["m01"] / M2["m00"]))
# draw the rectangle and centroid on the frame,
# then update the list of tracked points
cv2.rectangle(frame, (int(x), int(y)), (int(x+w), int(y+h)),(0, 255, 255), 2)
cv2.circle(frame, center, 5, (0, 0, 255), -1)
cv2.rectangle(frame, (int(x2), int(y2)), (int(x2+w2), int(y2+h2)),(0, 255, 255), 2)
cv2.circle(frame, center2, 5, (0, 0, 255), -1)
# update the points queue
pts.appendleft(center)
if y-15 < y2 < y+15:
pyautogui.click(4908, 984)
time.sleep(2)
y2 = 0
cv2.imshow("frame", frame)
key = cv2.waitKey(1)
if cv2.waitKey(25) & 0xFF == ord("q"):
cv2.destroyAllWindows()
break
But before the round or between rounds i get error
Traceback (most recent call last):
File "C:\Users\Game\Desktop\Py\Fish.py", line 74, in <module>
c2 = max(cnts2, key=cv2.contourArea)
ValueError: max() arg is an empty sequence
How can it be solved?
Going to change the two masks in one as Furas adviced
mask = cv2.bitwise_or(mask1, mask2)
But what can i use to find both max for green field and fish? till now it was 2 masks, 2 cnts and 2 max values.
With mss sct.grab i have not so great FPS (average 25fps) any other better capturing methods?
Thanks a lot!