18834161486 发表于 2024-3-21 17:25

基于密度聚类算法的通用怪物定位实现

先上代码
import cv2
import numpy as np
import wx
import wx.lib.scrolledpanel as scrolled
import wx.aui
from sklearn.cluster import DBSCAN


class MyFrame(wx.Frame):
    def __init__(self, parent, title):
      super(MyFrame, self).__init__(parent, title=title, size=(800, 600))
      self.mgr = wx.aui.AuiManager(self)
      # 创建第一个图片显示面板
      panel1 = scrolled.ScrolledPanel(self, -1)
      panel1.SetupScrolling()
      image_path1 = "1.jpg"
      self.image_bitmap1 = wx.Bitmap(image_path1, wx.BITMAP_TYPE_ANY)
      self.static_bitmap1 = wx.StaticBitmap(panel1, -1, self.image_bitmap1, pos=(10, 10))
      sizer = wx.BoxSizer(wx.HORIZONTAL)
      sizer.Add(self.static_bitmap1, 0, wx.ALL, 5)
      panel1.SetSizer(sizer)
      self.mgr.AddPane(panel1, wx.aui.AuiPaneInfo().Caption("Image 1").Left().BestSize(400, 600))

      self.Bind(wx.EVT_CONTEXT_MENU, self.on_context_menu)


      # 创建第二个图片显示面板
      panel2 = scrolled.ScrolledPanel(self, -1)
      panel2.SetupScrolling()
      self.image_bitmap2 = self.image_bitmap1# 直接从第一张图片获取数据
      self.static_bitmap2 = wx.StaticBitmap(panel2, -1, self.image_bitmap2, pos=(10, 10))
      sizer = wx.BoxSizer(wx.HORIZONTAL)
      sizer.Add(self.static_bitmap2, 0, wx.ALL, 5)
      panel2.SetSizer(sizer)
      self.mgr.AddPane(panel2, wx.aui.AuiPaneInfo().Caption("Image 2").Right().BestSize(400, 600))

      # 创建输入框面板
      input_panel = wx.Panel(self)
      input_panel.SetMinSize((400, 600))# 设置最小宽高为400x600
      input_sizer = wx.BoxSizer(wx.HORIZONTAL)
      self.input1 = wx.TextCtrl(input_panel, -1, "")
      self.input2 = wx.TextCtrl(input_panel, -1, "")
      self.input3 = wx.TextCtrl(input_panel, -1, "")
      self.output_button = wx.Button(input_panel, label="显示")
      self.output_button.Bind(wx.EVT_BUTTON, self.on_output_button_click)
      input_sizer.Add(self.input1, 0, wx.ALL, 5)
      input_sizer.Add(self.input2, 0, wx.ALL, 5)
      input_sizer.Add(self.input3, 0, wx.ALL, 5)
      input_sizer.Add(self.output_button, 0, wx.ALL, 5)
      input_panel.SetSizer(input_sizer)
      self.mgr.AddPane(input_panel, wx.aui.AuiPaneInfo().Caption("Inputs").Bottom().BestSize(400, 50))
      self.mgr.Update()

    def on_context_menu(self, event):
      menu = wx.Menu()
      item = menu.Append(-1, "Replace Image")
      self.Bind(wx.EVT_MENU, self.on_replace_image, item)
      self.PopupMenu(menu)
      menu.Destroy()

    def on_replace_image(self, event):
      with wx.FileDialog(self, "Choose a file", wildcard="All files (*.*)|*.*",
                           style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as fileDialog:
            if fileDialog.ShowModal() == wx.ID_CANCEL:
                return
            pathname = fileDialog.GetPath()
            self.update_image(pathname)
            new_bitmap2 = wx.Bitmap(pathname, wx.BITMAP_TYPE_ANY)
            self.image_bitmap2 = new_bitmap2

    def update_image(self, image_path):
      new_bitmap = wx.Bitmap(image_path, wx.BITMAP_TYPE_ANY)
      self.static_bitmap1.SetBitmap(new_bitmap)
      self.static_bitmap2.SetBitmap(new_bitmap)

    def on_output_button_click(self, event):
      input1_content = self.input1.GetValue()
      input2_content = self.input2.GetValue()
      input3_content = self.input3.GetValue()

      # 获取第二张图片数据
      image = self.bitmap_to_cv_image(self.image_bitmap2)
      processed_img = self.Stressshow(image, input1_content)
      processed_img = self.jl(processed_img, int(input2_content), int(input3_content))

      # 清空第二张图片面板数据
      self.static_bitmap2.SetBitmap(wx.Bitmap())

      # 显示处理后的图片在第二个图片面板中
      self.show_processed_image(processed_img, self.static_bitmap2)

    def show_processed_image(self, processed_image, image_panel):
      bitmap = self.cv_image_to_bitmap(processed_image)
      image_panel.SetBitmap(bitmap)

    def bitmap_to_cv_image(self, bitmap):
      bmp = bitmap.ConvertToImage()
      data = bmp.GetData()
      image = np.frombuffer(data, dtype=np.uint8)
      image = image.reshape(bmp.GetHeight(), bmp.GetWidth(), -1)
      image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
      return image

    def cv_image_to_bitmap(self, image):
      if len(image.shape) == 2:# 处理灰度图像
            height, width = image.shape
            cv2_image_rgb = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)# 将灰度图像转换为RGB格式
      elif len(image.shape) == 3:# 处理彩色图像
            height, width, _ = image.shape
            cv2_image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)# 将BGR格式转换为RGB格式
      else:
            raise ValueError('Unsupported image format')

      wx_image = wx.Image(width, height, cv2_image_rgb.tobytes())# 创建wx.Image对象
      bitmap = wx.Bitmap(wx_image)# 将wx.Image转换为Bitmap对象
      return bitmap

    def jl(self, img, eps=7, min=60):
      white_pixels = np.argwhere(img == 255)
      # # 使用DBSCAN进行像素聚类
      dbscan = DBSCAN(eps=eps, min_samples=min)
      labels = dbscan.fit_predict(white_pixels)
      for label in np.unique(labels):
            if label == -1:
                continue
            cluster = white_pixels
            x, y, w, h = cv2.boundingRect(cluster)
            img = cv2.rectangle(img, (y, x), (y + h, x + w), (125, 0, 0), 2)
      return img

    def parse_custom_color(self, color_string):
      r, g, b = int(color_string, 16), int(color_string, 16), int(color_string, 16)
      if len(color_string) > 6:
            dr, dg, db = int(color_string, 16), int(color_string, 16), int(color_string, 16)
      else:
            dr, dg, db = 0, 0, 0
      lower_rgb = np.array(, dtype=np.uint8)
      upper_rgb = np.array(, dtype=np.uint8)
      return lower_rgb, upper_rgb

    def Stressshow(self, img, colors):
      colors = colors.split("|")
      masks = np.zeros_like(img[:, :, 0], dtype=np.uint8)# 创建与图像大小相同的空白掩膜
      for color in colors:
            lower_rgb, upper_rgb = self.parse_custom_color(color)
            mask = cv2.inRange(img, lower_rgb, upper_rgb)
            masks += mask
      # 将符合条件的颜色设为白色(255),不符合条件的设为黑色(0)
      result = np.where(masks > 0, 255, 0).astype(np.uint8)
      return result


app = wx.App(False)
frame = MyFrame(None, "My GUI")
frame.Show()
app.MainLoop()

再说一下原理:
首先呢,通过颜色的rgb值将怪物从地图上区分出来,那么受这个因素影响,怪物的颜色就不能和地图颜色太相近,要不然就分辨不出来。这里其实稍微做了一下优化,可以像大漠那样取多个点的颜色,也支持偏色。
然后呢,将怪物的颜色变为白色,将地图颜色变为黑色,这就可以把图像转换为一个二维数组了,直接进行数据处理。
接着呢,使用密度聚类算法筛选出符合条件的区域。这里关于密度聚类算法大家可以百度一下就能找到原理。
最后呢,我们需要传进去三个参数,一个是怪物的颜色描述,一个是密度聚类的半径大小,一个是密度聚类的点的数量,就看可以筛选出来怪物的位置。
这三个参数需要大家通过微调来实现精准控制。
讲一讲软件用法:
需要的库大家自行下载,直接运行会报错缺少图片,这个不影响,直接对最左边的窗口右键可以替换图片,然后在最下面的三个输入框输入我们需要的参数,点击按钮就可以在右边的窗口框选出结果。
因为论坛不支持bmp格式图片,就不附图了。

gusong125 发表于 2024-3-21 23:46

强悍,支持楼主,不知道有没有寻路的解决方案基于图色的!
页: [1]
查看完整版本: 基于密度聚类算法的通用怪物定位实现