Показать сообщение отдельно
  #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)!

Удачи)
Ответить с цитированием