Форум по Delphi программированию

Delphi Sources



Вернуться   Форум по Delphi программированию > Все о Delphi > Графика и игры
Ник
Пароль
Регистрация <<         Правила форума         >> FAQ Пользователи Календарь Поиск Сообщения за сегодня Все разделы прочитаны

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
  #1  
Старый 07.10.2009, 12:11
alexnov alexnov вне форума
Прохожий
 
Регистрация: 16.04.2009
Сообщения: 33
Репутация: 10
Вопрос Как получить количество точек каждого цвета

Доброго времени суток!
Задача состоит в следующем: из загруженной картинки (для начала BMP) необходимо получить количество пикселов каждого цвета, которые содержит картинка. Задача простая, но как решить не знаю, с графикой дела не имел! Поможите плиз! ))
Ответить с цитированием
  #2  
Старый 07.10.2009, 15:07
Аватар для The Shadow
The Shadow The Shadow вне форума
Продвинутый
 
Регистрация: 11.06.2007
Адрес: Уфа, Россия
Сообщения: 793
Репутация: 35
По умолчанию

самый простой способ - перебор, может есть и другие
Код:
function ColorCount(const Pic: TImage; const Color: TColor): Integer;
var
  i, j: Integer;
begin
  Result:=0;
  for i:=1 to Pic.Width do
  for j:=1 to Pic.Height do
  if Pic.Canvas.Pixels[i][j] = Color then
  Inc(Result);
end;
зы. не проверял
__________________
Что делать, когда сломался комп:
1. Если вы юзер - делать ноги.
2. Если ремонтник - делать деньги.
3. Если вы программист - делать вид, что так было задумано.
Ответить с цитированием
  #3  
Старый 07.10.2009, 15:28
alexnov alexnov вне форума
Прохожий
 
Регистрация: 16.04.2009
Сообщения: 33
Репутация: 10
По умолчанию

неее, нужно посчитать сколько всего цветов в картинке и вывести какие это конкретно цвета! Как посчитать сколько цветов я нашел:

Код:
function HowManyColors(Bitmap: TBitmap): Integer;
type
  TRGB = record
    B, G, R: Byte;
  end;
  pRGB = ^TRGB;

var
  i: Byte;
  x, y: Integer;
  Dest: pRGB;
  RGBArray: array[0..255, 0..255] of array of Byte;
begin
  Bitmap.PixelFormat := pf24Bit;
  Result := 0;
  for y := 0 to Bitmap.Height - 1 do
  begin
    Dest := Bitmap.ScanLine[y];
    for x := 0 to Bitmap.Width - 1 do
    begin
      with Dest^ do
        if RGBArray[r, g] <> nil then
          for i := 0 to High(RGBArray[r, g]) do
          begin
            //если такой цвет уже есть, то выходим из цыкла
            if RGBArray[r, g][i] = b then
              Break;
            //если это последний круг цикла, то такого цвета нет
            if i = High(RGBArray[r, g]) then
            begin
              Inc(Result); //прибавляем один цвет
              SetLength(RGBArray[r, g], Length(RGBArray[r, g]) + 1);
              RGBArray[r, g][High(RGBArray[r, g])] := b;
            end;
          end
        else
        begin
          Inc(Result);
          SetLength(RGBArray[r, g], 1);
          RGBArray[r, g][0] := b;
        end;
      Inc(Dest);
    end;
  end;
end;

Пример использования: 
procedure TForm1.MMHowManyColorsClick(Sender: TObject);
var
  str: string;
begin
  Screen.Cursor := crHourGlass;
  try
    str := Format('Изображение содержит %d цветов.', [HowManyColors(FBitmap)]);
  finally
    Screen.Cursor := crDefault;
  end;
  Application.MessageBox(PChar(str), PChar(Application.Title), MB_OK);
end;


А вот как получить эти цета??? То есть например если цветов на картинке встречается 3 штуки, то вывести:
Цвет 1 - код цвета;
Цвет 2 - код цвета;
Цвет 3 - код цвета;
Ответить с цитированием
  #4  
Старый 07.10.2009, 15:54
Аватар для The Shadow
The Shadow The Shadow вне форума
Продвинутый
 
Регистрация: 11.06.2007
Адрес: Уфа, Россия
Сообщения: 793
Репутация: 35
По умолчанию

что нибудь такое (неоптимизированно, скорее всего есть варианты вроде того, что вы привели в качестве примера для вычисления числа цветов изображения )
Код:
var
  i, j: Cardinal;
  Colors: array [$FF000000..$FFFFFFFF] of integer;
begin
  for i:=1 to Image.Picture.Width do
  for j:=1 to Image.Picture.Height do
  Inc(Colors[Image.Canvas.Pixels[i, j]]);
  for i:=$FF000000 to $FFFFFFFF do
  if Colors[Image.Canvas.Pixels[i, j]] > 0 then
  Memo1.Lines.Add(IntToStr(i) + ' ' + IntToStr(Colors[i]) + ' раз');
__________________
Что делать, когда сломался комп:
1. Если вы юзер - делать ноги.
2. Если ремонтник - делать деньги.
3. Если вы программист - делать вид, что так было задумано.
Ответить с цитированием
  #5  
Старый 07.10.2009, 16:28
alexnov alexnov вне форума
Прохожий
 
Регистрация: 16.04.2009
Сообщения: 33
Репутация: 10
По умолчанию

Переполнение стека пишет! А как приведенный мною код поправить как мне надо??? Я чето не могу понять что там делается...
Ответить с цитированием
  #6  
Старый 07.10.2009, 16:33
Аватар для The Shadow
The Shadow The Shadow вне форума
Продвинутый
 
Регистрация: 11.06.2007
Адрес: Уфа, Россия
Сообщения: 793
Репутация: 35
По умолчанию

со ScanLine'ми не работал, вроде в факе были примеры какие то
__________________
Что делать, когда сломался комп:
1. Если вы юзер - делать ноги.
2. Если ремонтник - делать деньги.
3. Если вы программист - делать вид, что так было задумано.
Ответить с цитированием
  #7  
Старый 30.10.2009, 23:11
Asinkrit Asinkrit вне форума
Местный
 
Регистрация: 29.10.2009
Сообщения: 446
Репутация: 271
По умолчанию

Цитата:
Сообщение от The Shadow

Colors: array [$FF000000..$FFFFFFFF] of integer;

[/code]

)))

при отработке алгоритма, потребуется создание массива длинною 16 777 216 элементов, и потребуется выделения памяти более чем на 67 мегабайт)))
Лучше создать пустой массив и добавлять в него найденные цвета, в данном случае максимальное кол-во цветов не превысит Height * Width.

Обращение через Canvas.Pixels[,] - оооочень медленное, в данном случае ScanLine работает в сотню! раз быстрее)
Ответить с цитированием
  #8  
Старый 31.10.2009, 01:48
Asinkrit Asinkrit вне форума
Местный
 
Регистрация: 29.10.2009
Сообщения: 446
Репутация: 271
По умолчанию

Цитата:
Сообщение от alexnov
Доброго времени суток!
Задача состоит в следующем: из загруженной картинки (для начала BMP) необходимо получить количество пикселов каждого цвета, которые содержит картинка. Задача простая, но как решить не знаю, с графикой дела не имел! Поможите плиз! ))

Очень понравилась задача, решил попробовать свои силы)

Идея проста, но сложно реализована для повышения производительности.

желательно на время работы функции блокировать вызывающий ее обработчик.

Код:
function TForm1.HowManyColors(const Bitmap: TBitmap; const Memo:TMemo): Integer;
type //это лучше определять в начале модуля

  TRGB = record
    B, G, R: Byte;
  end;

  ARGB = array[0..1] of TRGB; 
  PARGB = ^ARGB;

const
  CArrayLengthInc = 10000;


type //а это в функции
  TColorCnt = record
    FColor:TColor;       //Одна ячейка массива содержит цвет
    FCount:Integer;      //и его кол-во
  end;

var
  x, y, i, l, tmp:integer;
  p: PARGB;

  Colors: Array of TColorCnt;  //Массив всех найденых цветов

begin
  //Подготовимся)
  l:=CArrayLengthInc;
  SetLength(Colors,l); //для начала создадим массив на 10000 элементов
  //Функция SetLength будет тормозить весь процесс, лучше 
  //ее вызывать поменьше)
  with Colors[0] do
    begin
    FColor:=0; //один цвет (черный) определим заранее
    FCount:=0;
    end;
  Result:=1; //Result - кол-во найденных элементов)
  with Bitmap do
    for y := 0 to Height - 1 do //перебираем строки массива(линии картинки)
      begin
      p := ScanLine[y];
      for x := 0 to Width - 1 do//перебираем элементы массива (пиксели)
         begin
         with p[x] do  tmp:=(r*g*b);  //берем цвет пикселя
         for i:=0 to Result - 1 do  //перебираем цвета
             if Colors[i].FColor = tmp then // если у нас есть такой цвет
               begin
               Inc(Colors[i].FCount);  //то увеличиваем у него счетчик
               Break;    //и покидаем цикл перебора цветов
               end;
         if Length(Colors)=Result then //иначе проверяем не достигнут ли 
           begin                              //конец массива
           Inc(l,CArrayLengthInc);  //если да, то увеличиваем его еще
           SetLength(Colors, l);     //на 10000 элементов
           end;
         with Colors[Result] do //и сохраняем новый цвет
           begin
           FColor:=tmp;
           FCount:=1;
           end;
         Inc(Result);
         //результаты можно вставить вывод результата статуса
         // выполнения данной функции, к примеру если ты хочешь
         //показывать состояние выполнения)
         Application.ProcessMessages; //Это что бы приложение не висло
         end;
      end;
  
  //Вот вывести все элементы - безумие, так как цветов может быть 
  //очень большое кол-во и займет это чуть меньше времени, чем 
  //первая чать функции, я бы отказался от этой затеи)
  
  with Memo.Lines do
    begin                                   
    BeginUpdate;
    Clear;
    if (Result-Ord(Colors[0].FCount=0))>0 then
      for i:=0+Ord(Colors[0].FCount=0) to Result-1 do
        with Colors[i] do
          Add('Код цвета: '+IntToStr(FColor)+' - '+IntToStr(FCount)+' раз');
    EndUpdate;
    end;
  //пора покинуть функцию, но вдруг у картинки не было черного цвета, 
  if Colors[0].FCount = 0 then Dec(Result); // не будем врать)
  SetLength(Colors,0);  //и освободим массив
end;


procedure TForm1.MMHowManyColorsClick(Sender: TObject);
begin
  ShowMessage('Изображение содержит '+Inttostr(
    HowManyColors(Image1.Picture.Bitmap,Memo1))+' цветов');
end;

При больших и многокрасочных изображениях, процесс выполнения будет длительным (в этом случае, как минимум стоит удалить вывод всех элементов, блок with Memo Lines do begin .. end)!

Удачи)

Последний раз редактировалось Asinkrit, 31.10.2009 в 01:56.
Ответить с цитированием
  #9  
Старый 02.11.2009, 09:58
alexnov alexnov вне форума
Прохожий
 
Регистрация: 16.04.2009
Сообщения: 33
Репутация: 10
По умолчанию

А как решить вопрос с памятью? При загрузке файла размером больше 500 мб, выскакивает сообщение о нехватке памяти!
Ответить с цитированием
  #10  
Старый 02.11.2009, 10:18
Аватар для Aristarh Dark
Aristarh Dark Aristarh Dark вне форума
Модератор
 
Регистрация: 07.10.2005
Адрес: Москва
Сообщения: 2,907
Версия Delphi: Delphi XE
Репутация: выкл
По умолчанию

Да не нужно загружать картинку вообще, работай напрямую с файлом.
__________________
Некоторые программисты настолько ленивы, что сразу пишут рабочий код.

Если вас наказали ни за что - радуйтесь: вы ни в чем не виноваты.
Ответить с цитированием
  #11  
Старый 02.11.2009, 10:23
alexnov alexnov вне форума
Прохожий
 
Регистрация: 16.04.2009
Сообщения: 33
Репутация: 10
По умолчанию

Цитата:
Сообщение от Aristarh Dark
Да не нужно загружать картинку вообще, работай напрямую с файлом.
А что есть разница???? Проверь плиз у себя будет этот код работать с файлом в 600 мб????!!!
Ответить с цитированием
  #12  
Старый 02.11.2009, 12:42
Аватар для Aristarh Dark
Aristarh Dark Aristarh Dark вне форума
Модератор
 
Регистрация: 07.10.2005
Адрес: Москва
Сообщения: 2,907
Версия Delphi: Delphi XE
Репутация: выкл
По умолчанию

Можешь мне не верить, но будет работать вполне приемлимо
Вот только писать код только ради того чтобы проверить его на скорость - мне как-то лениво.
__________________
Некоторые программисты настолько ленивы, что сразу пишут рабочий код.

Если вас наказали ни за что - радуйтесь: вы ни в чем не виноваты.
Ответить с цитированием
  #13  
Старый 02.11.2009, 16:16
alexnov alexnov вне форума
Прохожий
 
Регистрация: 16.04.2009
Сообщения: 33
Репутация: 10
По умолчанию

Тогда вопрос что значит работать с файлом напрямую? Соответствует ли этому принципу последний код, написанный Asinkrit'ом???
Ответить с цитированием
  #14  
Старый 02.11.2009, 16:49
Аватар для AleD
AleD AleD вне форума
Активный
 
Регистрация: 21.02.2009
Адрес: г.Краснокаменск
Сообщения: 383
Репутация: 91
По умолчанию

Цитата:
Сообщение от alexnov
Тогда вопрос что значит работать с файлом напрямую? Соответствует ли этому принципу последний код, написанный Asinkrit'ом???
Asinkrit предварительно загружает битмап, если как говорится использовать битмап в 600мб это чистое насилование компа, точнее оперативки. Описание битмапа можно найти в модуле windows.pas, считывать по ~16384 пикселей и искать цвета.
__________________
TAleD = class(TUser)
public
function HelpMe(ASubject, ARequest: String): String;
function GiveMeExample(ASubject: String): TStringList;
procedure WriteReview(APost: Integer; ADescription: TStringList);
end;
Ответить с цитированием
  #15  
Старый 02.11.2009, 17:27
Аватар для Aristarh Dark
Aristarh Dark Aristarh Dark вне форума
Модератор
 
Регистрация: 07.10.2005
Адрес: Москва
Сообщения: 2,907
Версия Delphi: Delphi XE
Репутация: выкл
По умолчанию

Цитата:
Сообщение от AleD
считывать по ~16384 пикселей и искать цвета
А еще лучше считывать по n строк картинки, тогда не нужно заморачиваться в рассчетах выравнивания.
__________________
Некоторые программисты настолько ленивы, что сразу пишут рабочий код.

Если вас наказали ни за что - радуйтесь: вы ни в чем не виноваты.
Ответить с цитированием
Ответ


Delphi Sources

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB-коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход


Часовой пояс GMT +3, время: 20:47.


 

Сайт

Форум

FAQ

Соглашения

Прочее

 

Copyright © Форум "Delphi Sources" by BrokenByte Software, 2004-2025