Что нового

Определение "среднего" цвета области

irvin12345

Новичок
Сообщения
30
Репутация
2
Есть картинка-лабиринт (600х600 точек или 50х50 ячеек).
Сразу обращаю внимание, картинка с шумом и проходимое/непроходимое поля состоят из одних и тех же цветов в разной концентрации).
Ее нужно перевести в двумерный булевый массив и дальше уже решать лабиринт.

Собственно вопрос: можно ли каким-либо образом получить "средний" цвет указанной области картинки?
 

rusreg79

Продвинутый
Сообщения
159
Репутация
57
Я в фотошопе определяю средний цвет: инструмент Пипетка с размером области больше точки, например 5х5
 
Автор
I

irvin12345

Новичок
Сообщения
30
Репутация
2
Спасибо за ответы, а можно примеры кода? или ссылку на подобное, если где-то уже было?
 

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
irvin12345 [?]
перевести в двумерный булевый массив
Что такое "средний" цвет не знаю. И как его найти - тоже. :-[
Сделал так: перевёл в чёрно-белое и подсчитал количество пикселей в сетке. Для данного конкретного случая очень даже получилось.
Код:
#Include <WinAPIEx.au3>
#Include <ScreenCapture.au3>
#Include <Array.au3>

$PicWidth = 197
$PicHeight = 107
$Cell = 15
$Percent = 60
$W = Floor($PicWidth/$Cell)
$H = Floor($PicHeight/$Cell)
Dim $Matrix[$W][$H]

$hGUI = GUICreate("test", $PicWidth, $PicHeight)
$Pic = GUICtrlCreatePic(@ScriptDir & "\labirint.jpg", 0, 0, $PicWidth, $PicHeight)
GUISetState()
WinWaitActive($hGUI)

$giBMPFormat = $GDIP_PXF04INDEXED
$hBmp = _ScreenCapture_CaptureWnd(@ScriptDir & "\test.bmp", ControlGetHandle($hGUI, "", $Pic), 0, 0, -1, -1, False)
GUICtrlDelete($Pic)
$Pic = GUICtrlCreatePic(@ScriptDir & "\test.bmp", 0, 0, $PicWidth, $PicHeight)
$hDC = _WinAPI_GetDC($hGUI)

For $i = 0 To $W-1
  For $j = 0 To $H-1
    $Matrix[$i][$j] = Count($i, $j)
  Next
Next

_ArrayDisplay($Matrix, "Matrix", -1, 1)

Func Count($k, $l)
  Local $Pix = 0
  For $i = 0 To $Cell-1
    For $j = 0 To $Cell-1
      If _WinAPI_GetPixel($hDC, $i+$k*$Cell, $j+$l*$Cell) = 8421504 Then $Pix += 1
    Next
  Next
  If $Pix/($Cell*$Cell)*100 >= $Percent Then
    Return 1 ; &" ("&Round($Pix/($Cell*$Cell)*100)&"%)"
  Else
    Return 0 ; &" ("&Round($Pix/($Cell*$Cell)*100)&"%)"
  EndIf
EndFunc
 
Автор
I

irvin12345

Новичок
Сообщения
30
Репутация
2
Сделал так: перевёл в чёрно-белое и подсчитал количество пикселей в сетке. Для данного конкретного случая очень даже получилось
Огромное спасибо за код, на реальном примере хоть и выполняется 15 секунд, но это пока не критично.

Сразу вопрос. Вы преобразовали картинку в "чёрно-белое" до выполнения скрипта? Просто не вижу в коде этого преобразования и при выполнении получается массив нулей.

Что такое "средний" цвет не знаю. И как его найти - тоже.
Как я это представляю: Взять пиксели области, разложить на R,G,B, просуммировать R1+R2+R3...,G1+G2+G3...,B1+B2+B3... , поделить на количество пикселей, сложить получившиеся R,G,B обратно в цвет.
 

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
irvin12345 [?]
преобразовали картинку в "чёрно-белое"
Картинку "преобразует" функция
Код:
_ScreenCapture_CaptureWnd()
при сохранении в файл test.bmp. У меня указан формат
Код:
$giBMPFormat = $GDIP_PXF04INDEXED
четыре бита на пиксель. Получается чёрно-белое.
 
Автор
I

irvin12345

Новичок
Сообщения
30
Репутация
2
при сохранении в файл test.bmp. У меня указан формат четыре бита на пиксель. Получается чёрно-белое.
Тогда странно, у меня что-то не выходит. Ну да ладно, теперь знаю куда копать и чем пользоваться. спасибо.
 

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
irvin12345 [?]
Взять пиксели области, разложить на R,G,B, просуммировать R1+R2+R3...,G1+G2+G3...,B1+B2+B3... , поделить на количество пикселей, сложить получившиеся R,G,B обратно в цвет.
Код:
#Include <WinAPIEx.au3>
#Include <Color.au3>
#Include <Array.au3>

$PicWidth = 197
$PicHeight = 107
$Cell = 15
$W = Floor($PicWidth/$Cell)
$H = Floor($PicHeight/$Cell)
Dim $Matrix[$W][$H]

$hGUI = GUICreate("test", $PicWidth, $PicHeight)
$Pic = GUICtrlCreatePic(@ScriptDir & "\labirint.jpg", 0, 0, $PicWidth, $PicHeight)
GUISetState()
WinWaitActive($hGUI)
$hDC = _WinAPI_GetDC($hGUI)

For $i = 0 To $W-1
  For $j = 0 To $H-1
    $Matrix[$i][$j] = Count($i, $j)
  Next
Next

_ArrayDisplay($Matrix, "Matrix", -1, 1)

Func Count($k, $l)
  Local $PixColor, $R=0, $G=0, $B=0
  For $i = 0 To $Cell-1
    For $j = 0 To $Cell-1
      $PixColor = _ColorGetRGB(_WinAPI_GetPixel($hDC, $i+$k*$Cell, $j+$l*$Cell))
      $R += $PixColor[0]
      $G += $PixColor[1]
      $B += $PixColor[2]
    Next
  Next
  Dim $aColor[3] = [$R/($Cell*$Cell), $G/($Cell*$Cell), $B/($Cell*$Cell)]
  Return Round(_ColorSetRGB($aColor)) ; & " (0x" & Hex(Round(_ColorSetRGB($aColor)), 6) & ")"
EndFunc

irvin12345 [?]
у меня что-то не выходит
А что конкретно не выходит? В папке со скриптом файл test.bmp появляется? Он чёрно-белый?
 
Автор
I

irvin12345

Новичок
Сообщения
30
Репутация
2
А что конкретно не выходит? В папке со скриптом файл test.bmp появляется? Он чёрно-белый?
Нет, не появляется. Подозреваю что дело в подключаемых *.au3.
Если WinAPIEx.au3 мне еще пришлось скачать с сайта, то остальные видимо есть в стандартной поставке.
 

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
irvin12345 [?]
на реальном примере
Может с этим связано? Я ведь делал для картинки, приложенной к сообщению.
Выкладывайте свой реальный пример. Посмотрим.
 
Автор
I

irvin12345

Новичок
Сообщения
30
Репутация
2
Может с этим связано? Я ведь делал для картинки, приложенной к сообщению.
Выкладывайте свой реальный пример. Посмотрим.
На том, что в первом посте, такая же ситуация. Так что проблема не в этом.
 

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
Проверил на WindowsXP. Действительно, проблема есть. И она вот в этом
Код:
$giBMPFormat = $GDIP_PXF04INDEXED
К сожалению, в графике я не силён и объяснить, почему на ХР не работает, не могу. :(
Но скрипт с определение "среднего" цвета вроде работает нормально, хоть и долго. Для создания матрицы можно "средние" значения использовать - там диапазоны чётко видны.
И будем надеяться на помощь специалистов по графике.
 
Автор
I

irvin12345

Новичок
Сообщения
30
Репутация
2
Re: Определение \"среднего\" цвета области

Потратил пол дня на попытки разобраться с проблемой, но так ничего и не получилось. В итоге пошел искать другие варианты.
Нашлось это http://autoit-script.ru/index.php/topic,446.0.html (там пример 1). Поэкспериментировал с ним и оказалось что перевод в 256 оттенков серого он делает как RGB источника = GGG приемника. Те полностью игнорирует R и B цвета.
Результат получается вполне приличный и более ощущаемый "на глаз". Но при таком-то алгоритме необходимость подгружать библиотеку, пересохранять/перезагружать изображение при том, что я и так могу использовать для анализа только G составляющую, нет смысла ей пользоваться в данном случае.


Добавлено:
Сообщение автоматически объединено:

И что касается темы, проблема еще не решена. Решение InnI - это перебор всех пикселей картинки и их анализ. Для моей задачи сошел и такой способ, но в целом это не вариант и хотелось бы увидеть более быстрые способы.
 
Автор
I

irvin12345

Новичок
Сообщения
30
Репутация
2
Если кому пригодится - то вот что получилось в итоге:
Код:
#include <GDIP.au3>
#include <GDIPlus.au3>
#include <Array.au3>

$Cell = 12
$W = 50
$H = 50
Dim $Matrix[$H][$W]
 
_GDIPlus_Startup()
$hImage = _GDIPlus_ImageLoadFromFile(@ScriptDir & '\test.JPG')
$iWidth = _GDIPlus_ImageGetWidth($hImage)
$iHeight = _GDIPlus_ImageGetHeight($hImage)
$iBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
$hBitmap = _GDIPlus_BitmapCreateFromHBITMAP($iBitmap)

For $k = 0 To $W-1
   For $l = 0 To $H-1
	  $iGreen = 0
	  For $i = 0 To $Cell-1
		 For $j = 0 To $Cell-1
			$iGreen += Dec(StringMid(Hex(_GDIPlus_BitmapGetPixel($hBitmap, $i+$k*$Cell, $j+$l*$Cell),6),3,2))
		 Next
	  Next
	  $Matrix[$k][$l] = Round($iGreen/($Cell*$Cell))
   Next
Next
   
_GDIPlus_ImageDispose($hImage)
_WinAPI_DeleteObject($iBitmap)
_WinAPI_DeleteObject($hBitmap)
_GDIPlus_Shutdown()
  
_ArrayDisplay($Matrix, "Matrix",-1,1)


~6 секунд на картинку 600*600 вполне приличный результат.
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Попробуйте не перебирать каждый пиксел в ячейке, а маштабировать ячейку из 12x12 в изображение 1x1 с использованием интерполяции (HALFTONE). В итоге вы получите матрицу с приблизительно одинаковыми темными и светлыми оттенками, которую, с помощью нехитрого анализа яркости цветов, можно легко превратить в матрицу из 0 и 1. Так как ваше изображение отличается от условий задачи (шаг 15x15), то я его немного обрезал (см. ниже).

Код:
#Include <APIConstants.au3>
#Include <Array.au3>
#Include <GDIPlus.au3>
#Include <WinAPIEx.au3>

$W = 195
$H = 105
$C = 15

Dim $Matrix[$H / $C][$W / $C]
Dim $HLS[3]

_GDIPlus_Startup()
$hImage = _GDIPlus_ImageLoadFromFile(@ScriptDir & '\195x105x15.jpg')
$hBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
_GDIPlus_ImageDispose($hImage)
_GDIPlus_Shutdown()

$hSrcDC = _WinAPI_CreateCompatibleDC(0)
$hSrcSv = _WinAPI_SelectObject($hSrcDC, $hBitmap)
$hDstDC = _WinAPI_CreateCompatibleDC(0)
$hDib = _WinAPI_CreateDIB(1, 1)
$hDstSv = _WinAPI_SelectObject($hDstDC, $hDib)
_WinAPI_SetStretchBltMode($hDstDC, $HALFTONE)

For $y = 0 To $H / $C - 1
	For $x = 0 To $W / $C - 1
		_WinAPI_StretchBlt($hDstDC, 0, 0, 1, 1, $hSrcDC, $x * $C, $y * $C, $C, $C, $SRCCOPY)
		$RGB = _WinAPI_GetPixel($hDstDC, 0, 0)
		_WinAPI_ColorRGBToHLS($RGB, $HLS[0], $HLS[1], $HLS[2])
		$Matrix[$y][$x] = $HLS[1]

		; Visual representation
		$hDC = _WinAPI_GetDC(0)
		$tRect = _WinAPI_CreateRectEx($x * $C, $y * $C, $C, $C)
		$hBrush = _WinAPI_CreateSolidBrush(_WinAPI_SwitchColor($RGB))
		_WinAPI_FillRect($hDC, DllStructGetPtr($tRect), $hBrush)
		_WinAPI_DeleteObject($hBrush)
		_WinAPI_ReleaseDC(0, $hDC)

	Next
Next

_WinAPI_SelectObject($hSrcDC, $hSrcSv)
_WinAPI_DeleteObject($hBitmap)
_WinAPI_DeleteDC($hSrcDC)
_WinAPI_SelectObject($hDstDC, $hDstSv)
_WinAPI_DeleteObject($hDib)
_WinAPI_DeleteDC($hDstDC)

_ArrayDisplay($Matrix, 'Luminance')

$Min = 240
$Max = 0

For $y = 0 To $H / $C - 1
	For $x = 0 To $W / $C - 1
		If $Min > $Matrix[$y][$x] Then
			$Min = $Matrix[$y][$x]
		EndIf
		If $Max < $Matrix[$y][$x] Then
			$Max = $Matrix[$y][$x]
		EndIf
	Next
Next

$Q = Round(($Min + $Max) / 2)

For $y = 0 To $H / $C - 1
	For $x = 0 To $W / $C - 1
		$Matrix[$y][$x] = Number($Matrix[$y][$x] < $Q)
	Next
Next

_ArrayDisplay($Matrix, 'Matrix')

_WinAPI_InvalidateRect(ControlGetHandle('[CLASS:Progman;TITLE:Program Manager]', '', 'SysListView321'))
 
Верх