Как найти пиксель нужного цвета с

0 / 0 / 0

Регистрация: 17.03.2014

Сообщений: 25

1

Обработка изображения: поиск пикселей определённого цвета

29.09.2015, 22:40. Показов 3073. Ответов 1


Студворк — интернет-сервис помощи студентам

Добрый вечер!
Имеется такое задание: есть изображение, нужно найти пиксели определенного цвета и записать их координаты в массив.

В принципе, задание не сложное, и логически понимаю, как все должно обрабатываться.
но есть несколько вопросов:
1) можно ли вообще сделать это в консоли? просто везде видел примеры обработки только на форме
2) и если да, то подскажите, пожалуйста, как «загрузить» изображение для обработки?



0



EvilFromHell

976 / 871 / 350

Регистрация: 26.04.2012

Сообщений: 2,647

29.09.2015, 23:01

2

Лучший ответ Сообщение было отмечено Studentiq как решение

Решение

Цитата
Сообщение от Studentiq
Посмотреть сообщение

1) можно ли вообще сделать это в консоли? просто везде видел примеры обработки только на форме

Для кода нет никакой разницы, как именно у вас устроен UI-консоль, форма, или вообще все отправляется куда-то в сеть

Цитата
Сообщение от Studentiq
Посмотреть сообщение

2) и если да, то подскажите, пожалуйста, как «загрузить» изображение для обработки?

C#
1
2
3
                        System.IO.FileStream fs = new System.IO.FileStream(filename, System.IO.FileMode.Open);
                        System.Drawing.Image img = System.Drawing.Image.FromStream(fs);
                        fs.Close();

При такой загрузке изображение загружается именно в память и не блокирует доступ к файлу. На счет обработки-GetPixel очень медленный, поэтому используйте более низкоуровневую конструкцию с LockBits. Тут, тут инфа для размышления



1



Okay, I am looking for a function or something that will read the color of a certain pixel on my monitor, and when that color is detected, another function will be enabled. I figure using RGB. All help appreciated. Thank You.

H H's user avatar

H H

261k30 gold badges329 silver badges514 bronze badges

asked Sep 27, 2009 at 16:51

Brandon's user avatar

5

This is the most efficient: It grabs a pixel at the location of the cursor, and doesn’t rely on only having one monitor.

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Diagnostics;

namespace FormTest
{
    public partial class Form1 : Form
    {
        [DllImport("user32.dll")]
        static extern bool GetCursorPos(ref Point lpPoint);

        [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
        public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);

        public Form1()
        {
            InitializeComponent();
        }

        private void MouseMoveTimer_Tick(object sender, EventArgs e)
        {
            Point cursor = new Point();
            GetCursorPos(ref cursor);

            var c = GetColorAt(cursor);
            this.BackColor = c;

            if (c.R == c.G && c.G < 64 && c.B > 128)
            {
                MessageBox.Show("Blue");
            }
        }

        Bitmap screenPixel = new Bitmap(1, 1, PixelFormat.Format32bppArgb);
        public Color GetColorAt(Point location)
        {
            using (Graphics gdest = Graphics.FromImage(screenPixel))
            {
                using (Graphics gsrc = Graphics.FromHwnd(IntPtr.Zero))
                {
                    IntPtr hSrcDC = gsrc.GetHdc();
                    IntPtr hDC = gdest.GetHdc();
                    int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, location.X, location.Y, (int)CopyPixelOperation.SourceCopy);
                    gdest.ReleaseHdc();
                    gsrc.ReleaseHdc();
                }
            }

            return screenPixel.GetPixel(0, 0);
        }
    }
}

Now, obviously, you don’t have to use the cursor’s current location, but this is the general idea.

EDIT:

Given the above GetColorAt function you can poll a certain pixel on the screen in a safe, performance friendly way like this:

private void PollPixel(Point location, Color color)
{
    while(true)
    {
        var c = GetColorAt(location);

        if (c.R == color.R && c.G == color.G && c.B == color.B)
        {
            DoAction();
            return;
        }

        // By calling Thread.Sleep() without a parameter, we are signaling to the
        // operating system that we only want to sleep long enough for other
        // applications.  As soon as the other apps yield their CPU time, we will
        // regain control.
        Thread.Sleep()
    }
}

You can wrap that in a Thread if you want, or execute it from a Console application. «Whatever suits your fancy,» I guess.

answered Sep 27, 2009 at 17:05

John Gietzen's user avatar

John GietzenJohn Gietzen

48.6k32 gold badges145 silver badges190 bronze badges

4

Most answers here use the very same source of that pixel (desktop dc).
The key function is GetPixel.

[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetWindowDC(IntPtr window);
[DllImport("gdi32.dll", SetLastError = true)]
public static extern uint GetPixel(IntPtr dc, int x, int y);
[DllImport("user32.dll", SetLastError = true)]
public static extern int ReleaseDC(IntPtr window, IntPtr dc);

public static Color GetColorAt(int x, int y)
{
    IntPtr desk = GetDesktopWindow();
    IntPtr dc = GetWindowDC(desk);
    int a = (int) GetPixel(dc, x, y);
    ReleaseDC(desk, dc);
    return Color.FromArgb(255, (a >> 0) & 0xff, (a >> 8) & 0xff, (a >> 16) & 0xff);
}

I think this is the cleanest and quickest way.

Note:

If you have modified the default text size among the Display Settings on Windows to increase readability on a high resolution display, the coordinate parameters of GetPixel() need to be adjusted the same way. For example, if the cursor location is (x,y) with 150% of text size on Windows 7, you need to call GetPixel(x*1.5, y*1.5) to get the color of the pixel under the cursor.

answered Jul 15, 2014 at 13:23

Bitterblue's user avatar

BitterblueBitterblue

12.9k16 gold badges85 silver badges124 bronze badges

2

This function is shorter and can achieve the same result using System.Drawing, without Pinvoke.

Color GetColorAt(int x, int y)
{
    Bitmap bmp = new Bitmap(1, 1);
    Rectangle bounds = new Rectangle(x, y, 1, 1);
    using (Graphics g = Graphics.FromImage(bmp))
        g.CopyFromScreen(bounds.Location, Point.Empty, bounds.Size);
    return bmp.GetPixel(0, 0);
}

Kirill Kobelev's user avatar

answered Apr 26, 2018 at 9:50

J3soon's user avatar

J3soonJ3soon

2,9432 gold badges27 silver badges49 bronze badges

1

Please check this two different functions I have used in one of my previous projects :

1) This function takes snapshot of Desktop

private void CaptureScreenAndSave(string strSavePath)
        {

            //SetTitle("Capturing Screen...");

            Bitmap bmpScreenshot;

            Graphics gfxScreenshot;
            bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height,System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            gfxScreenshot = Graphics.FromImage(bmpScreenshot);
            gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
            MemoryStream msIn = new MemoryStream();
            bmpScreenshot.Save(msIn, System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders()[0], null);

            msIn.Close();

            byte[] buf = msIn.ToArray();

            MemoryStream msOut = new MemoryStream();

            msOut.Write(buf, 0, buf.Length);

            msOut.Position = 0;

            Bitmap bmpOut = new Bitmap(msOut);

            try
            {
                bmpOut.Save(strSavePath, System.Drawing.Imaging.ImageFormat.Bmp);
                //SetTitle("Capturing Screen Image Saved...");
            }

            catch (Exception exp)
            {

            }

            finally
            {
                msOut.Close();
            }
        }

2) This function takes an image in input and calculates RGB average of pixel range given.

double GetRGBAverageForPixelRange( int istartRange, int iEndRange,  Bitmap oBitmap )
        {
            double dRetnVal = 0 ;
            Color oTempColor ; 
            int i, j ;
            for( int iCounter = istartRange ; iCounter < iEndRange ; iCounter++ )
            {
                i = (iCounter % (oBitmap.Width));
                j = ( iCounter / ( oBitmap.Width ) ) ;
                if (i >= 0 && j >= 0 && i < oBitmap.Width && j < oBitmap.Height )
                {
                    oTempColor = oBitmap.GetPixel(i, j);
                    dRetnVal = dRetnVal + oTempColor.ToArgb();
                }

            }
            return dRetnVal ;
        }

This two functions together might solve your problem. Happy Coding :)

EDIT : Please note that GetPixel is very slow function. I will think twice befor using it.

answered Sep 27, 2009 at 17:07

MRG's user avatar

MRGMRG

3,2191 gold badge26 silver badges35 bronze badges

As far as I know the easiest way to do this is to:

  1. take a screenshot
  2. look at the bitmap and get the pixel color

Edit

There is probably no way to «wait» until the pixel changes to a certain color. Your program will probably have to just loop and check it every so often until it sees the color.

For example:

while(!IsPixelColor(x, y, color))
{
    //probably best to add a sleep here so your program doesn't use too much CPU
}
DoAction();

EDIT 2

Here is some sample code you can modify. This code just changes the color of a label based on the current color in a given pixel. This code avoids the handle leak mentioned.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.InteropServices;

namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{

    [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
    public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);


    Thread t;
    int x, y;

    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        x = 20;
        y = 50;
        t = new Thread(update);
        t.Start();
    }

    private void update()
    {
        Bitmap screenCopy = new Bitmap(1, 1);
        using (Graphics gdest = Graphics.FromImage(screenCopy))
        {
            while (true)
            {
                //g.CopyFromScreen(new Point(0, 0), new Point(0, 0), new Size(256, 256));
                using (Graphics gsrc = Graphics.FromHwnd(IntPtr.Zero))
                {
                    IntPtr hSrcDC = gsrc.GetHdc();
                    IntPtr hDC = gdest.GetHdc();
                    int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, x, y, (int)CopyPixelOperation.SourceCopy);
                    gdest.ReleaseHdc();
                    gsrc.ReleaseHdc();
                }
                Color c = Color.FromArgb(screenCopy.GetPixel(0, 0).ToArgb());
                label1.ForeColor = c;
            }
        }
    }
}

}

answered Sep 27, 2009 at 16:56

tster's user avatar

tstertster

17.8k5 gold badges53 silver badges71 bronze badges

4

This line uses About 10 ms.

int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, location.X, location.Y, (int)CopyPixelOperation.SourceCopy);

answered Mar 11, 2021 at 18:20

nimanabavi321's user avatar

  • #1

Доброго времени суток,

уже давно пишу большого бота для игры (всё во флеше, никакого доступа к коду нет)… среди прочих действий боту нужно перейти в окно с картинкой… на которой необходимо найти графический объект и нажать его.

пока не влезал в дебри работы с цветом — решил до тупого просто: включил цикл пробега мышки по экрану и кликом в текущих координатах…
если там оказался объект то открывается дополнительное окно которое имеет свои уникальные данные которые подсказывают что вложенное окно открылось = объект найдет и открыт.

единственная проблема — объектов достаточно много… и просто рыскать по экрану в поисках «открылось или нет» весьма медленно.

покопался в хелпе — и нашёл функцию — PixelSearch

она великолепно и ОЧЕНЬ быстро нашла нужный цвет( один из 4х нужных), но тут обнаружились сразу 2 проблемы:

а) таких цветов может быть несколько и мне надо найти все (по возможности сохранить)
б) похоже функция PixelSearch ищет по Х-оси сначала..

вопрос1: можно ли как-то найти Следующий/ВСЕ пиксели нужного цвета?
вопрос2: можно ли «заставить» функцию PixelSearch — искать по Y-оси?

заранее спасибо.

Последнее редактирование: 9 Мар 2023

  • #2

можно ли как-то найти Следующий/ВСЕ пиксели нужного цвета?

На форуме можно найти различные реализации.

можно ли «заставить» функцию PixelSearch — искать по Y-оси?

Направление поиска определяется координатами. Читайте примечания в справке по функции.

  • #3

На форуме можно найти различные реализации.

«обожаю» ответы в духе «ищите лучше — в интернете есть всё»…
если вам не сложно — поделитесь линком на конкретный топик где реализовано то, что мне надо.

Направление поиска определяется координатами. Читайте примечания в справке по функции.

спасибо… уже прочитал… и опробовал.

  • #4

ищите лучше — в интернете есть всё

А разве не так? Или думаете, что вы первый, кому пришла в голову идея получить массив пикселей? :wink:

BmpSearch — Search for Bitmap within Bitmap — Assembly Version

Heres a function for searching for a bitmap within another bitmap. The heart of it is written assembly (source included) and working pretty quick I feel. I have included an example which is pretty basic and should be easily enough for anyone to get the concept. You will be given a small blue wind…


www.autoitscript.com

www.autoitscript.com

Сообщение автоматически объединено: 9 Мар 2023

можно ли «заставить» функцию PixelSearch — искать по Y-оси?

Когда-то делал _PixelSpiralSearch: поиск по спирали :cool:

[Мышь, клавиатура] PixelSearch от центра раб. стола

Кликер под игру Darkorbit.com Помогите пожалуйста с куском кода. Нужно чтоб скрипт искал Pixel от центра рабочего стола по спирали и кликал если нашел! Весь форум наверно перелистал но так и не нашел примеров((. Только мышкой по спирали пример нашел, пробовал переделать так не получилось!((…



autoit-script.ru

Последнее редактирование: 9 Мар 2023

  • #5

А разве не так? Или думаете, что вы первый, кому пришла в голову идея получить массив пикселей? :wink:

или я «слишком умный»… или… на этом мысль обрывается (с)

включил вечный цикл, поставил внутрь: Local $aCoord = PixelSearch(488, 862, 1415, 320, 0x4EB73C)

в случае нахождения сохраняю $aCoord[0] и $aCoord[1] в переменные
и на последок добавил проверку на повторяемость… и в случае не нахождения вовсе или не нахождения уникального — ExitLoop

всё работает штатно… пока что…
не претендую на звание автора самого изящного кода… но получилось как-то так…

$aaa = 0

While True

   Local $aCoord = PixelSearch(488, 862, 1415, 320, 0x4EB73C)

;~     0x237DD6 - blue event
;~     0x4EB73C - green event
;~     0xC358D4 - purple event
;~     0xCF63DE - purple event №2
;~     0xC58405 - gold event

    If Not @error Then

            $x0 = $aCoord[0]
            $y0 = $aCoord[1]

            If ($x0 <> $x1) and ($x0 <> $x2)  Then ;~ ограничился поиском 2х объектов с нужным цветом
            $aaa +=1

                    MouseMove($aCoord[0], $aCoord[1])  ;~ двигаем мышку к объекту чтоб убедиться зрительно что это нужный
                    Sleep (1000)
                    ConsoleWrite("Координата пикселя = " & $x0 & "," & $y0 & @CRLF) ; пишем в консоль в каких координатах нашли нужный пиксель

                    Switch $aaa
                        Case 1
                            $x1 = $x0
                            $y1 = $y0
                        Case 2
                            $x2 = $x0
                            $y2 = $y0
                        Case Else
                            ExitLoop
                    EndSwitch
            Else
                MsgBox(0, "END", "No more")
                MsgBox(0, "Calc total runs", $aaa)
                ExitLoop

            EndIf

    Else
          if $aaa <> 0 Then ;~ добавил защиту от неверного сообщения при нахождении только 1го объекта нужного цвета
                   MsgBox(0, "END", "No more")
                    MsgBox(0, "Calc tital", $aaa)
          Else
                    MsgBox(0, "FAIL", "Not found")
          EndIf

          ExitLoop

    EndIf

WEnd

Последнее редактирование: 9 Мар 2023

У меня стоит задача разобрать изображение на пиксели, для дальнейшей идентификации цвета каждого из них.

Обычный GetPixel мне не подходит из-за скорости. Я нашёл следующий код, но при работе с ним возникают проблемы, например, при загрузки изображений больше 64 пикселей вылетает исключение

System.AccessViolationException "попытка чтения в защищенную память"

на эту строчку: B = scan0[i + 0];, а если уменьшить разрешение картинки в фотошопе или в любом онлайн конвертере, программа неверно получает цвета и на выходе я получаю полную белиберду (цвета определены не верно и картинка вся перекошенная, а так же куча лишних пикселей), скорее всего это из за того, что функция работает только с 32bppArgb, но как это исправить не придумал.

Сам я никогда раньше не сталкивался с такой задачей, буду благодарен если сразу приведёте рабочее решение, чтобы функция могла «переварить» любое изображение.

Сам код:

public unsafe void GetRGBPixel(Bitmap bmp)
{
    int width = bmp.Width;
    int height = bmp.Height;
    BitmapData bData = bmp.LockBits(
        new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height),
        ImageLockMode.ReadWrite, bmp.PixelFormat);
    int bytesPerPixel = 4; 
    int maxPointerLenght = width * height * bytesPerPixel;
    byte R, G, B, A;
    byte* scan0 = (byte*)bData.Scan0.ToPointer();
    for (int i = 0; i < maxPointerLenght; i += 4)
    {
        B = scan0[i + 0];
        G = scan0[i + 1];
        R = scan0[i + 2];
        A = scan0[i + 3];
        RC[iterC] = R;
        GC[iterC] = G;
        BC[iterC] = B;
        iterC++;               
    }
    bmp.UnlockBits(bData);
}

Нужен был скрипт, который загрузит изображение и будет искать на нём заданный цвет. Если такой цвет есть, то вывести сообщение. Не особо красиво, зато работает. Исходник на Python, переменным r_query, g_query и b_query нужно присвоить цвет в формате RGB, переменной url — адрес изображения.

#! /usr/bin/env python
# -*- coding: utf-8 -*-
 
from PIL import Image
import urllib2
 
r_query = 177
g_query = 255
b_query = 70
url = 'https://getked.ru/files/line.png'
result = 'Цвет не найден'
 
img = Image.open(urllib2.urlopen(url))
rgb = img.convert('RGB')
for x in range(img.size[0]):
	for y in range(img.size[1]):
		r, g, b, = rgb.getpixel((x, y))
		if r == r_query and g == g_query and b == b_query:
			result = "Цвет найден"
#если пиксель нужного цвета найден, то прерываем поиск
 
print result

Понравилась статья? Поделить с друзьями:
  • Как составить уголок здоровья
  • Найти дверь по фотографии как
  • Как найти девушку которая даст полизать
  • Как найти кординаты точки пересечения прямых
  • Как правильно составить характеристику на работника образец для суда положительную