Rozpoznawanie pliku graficznego załadowanego do TImage

0

Ładuję do komponentu TImage co jakiś czas różne pliki graficzne (png) i chciałbym rozpoznać, który plik jest aktualnie załadowany. Próbowałem tak:

if Image1.Picture.Graphic.Equals(bitmapa1) then

ale nie wychodzi.

0

co to znaczy który plik jest aktualnie załadowany? Jak ładujesz, skąd?

0

Chodzi o porównanie zawartości, tak?

0

Załadowany instrukcją LoadFromFile(filename)
Chcę wiedzieć jaki plik graficzny jest obecnie w komponencie TIMage. Czyli znać albo nazwę pliku graficznego (może być ze ścieżką dostępu), albo czy dany plik graficzny np.

Var bitmap1:TBitmap;

bitmap1:=TBitmap.create;
bitmap1.loadfromfile(filename);

np. Bitmap1 jest aktualnie w TImage, czy też jakiś inny.

0

No to sobie tę ścieżkę gdzieś przechowaj – w czym problem?

A jak potrzebujesz porównać zawartość dwóch obrazów, to do tego celu służy metoda Equals.

0

Chodzi o to by tą ścieżkę odczytać z TImage.
Equals też nie wchodzi w grę.

Jest kilka plików graficznych, chodzi o to że nie wiem jaki obecnie jest w TImage i chcę odczytać z TImage jaki plik jest załadowany.

2

TImage nie przechowuje takiej informacji.
Równie dobrze możesz ustawić obraz z kamerki lub wkleić ze schowka - jak by była wtedy ścieżka?

0

A może to porównywanie obrazów byłoby możliwe.Jak jednak porównać do siebie dwie grafiki po ich wyglądzie czy są takie same?

1

Rozszerz sobie subclassingiem klasę TImage i dodaj do niej właściwość służącą do odczytu ścieżki ładowanego pliku oraz metodę, która załaduje obraz z pliku i zapamięta ścieżkę. I z głowy. ;)

type
  TImage = class(ExtCtrls.TImage)
  private
    FFileName: String;
  public
    procedure LoadImageStoreName(const AFileName: String);
    procedure DoneImageClearName();
  public
    property FileName: String read FFileName;
  end;

{..}

procedure TImage.LoadImageStoreName(const AFileName: String);
begin
  Picture.LoadFromFile(AFileName);
  FFileName := AFileName;
end;

procedure TImage.DoneImageClearName();
begin
  Picture.Clear();
  FFileName := '';
end;

I zamiast używać metody LoadFromFile, użyj LoadImageStoreName (drugiej do usunięcia obrazu i nazwy).

A jeśli tych kontrolek używasz w wielu formularzach to zamiast subclassingu, po prostu stwórz sobie własną kontrolkę dziedziczącą z TImage i rozszerz jej funkcjonalność w podobny sposób.

0

A czy przypadkiem nie chodzi Ci o coś takiego... detecting graphic formats

0
furious programming napisał(a):

/ciach/

A jak potrzebujesz porównać zawartość dwóch obrazów, to do tego celu służy metoda Equals.

Nooo... nie.
Metoda Equals został zaimplementowana na poziomie TObject w ten sposób:

function TObject.Equals(Obj: TObject): Boolean;
begin
  Result := Obj = Self;
end;

I służy do sprawdzenia czy podana referencja na obiekt jest tym samym obiektem z którego wywołujemy metodę Equals.
Ale na pewno nie porównuje ona dwóch obrazów.

0

Vcl.Graphics.TGraphic.Equals

Compare graphic to another TGraphic object and return true if objects contain same graphic.

Equals compares the contents of two TGraphic objects. Equals returns false if the TGraphic are different classes or if they contain different graphical data.

Dokumentacja kłamie? ;)

1
furious programming napisał(a):

Vcl.Graphics.TGraphic.Equals

Compare graphic to another TGraphic object and return true if objects contain same graphic.

Equals compares the contents of two TGraphic objects. Equals returns false if the TGraphic are different classes or if they contain different graphical data.

Dokumentacja kłamie? ;)

Pewnie ;)

TGraphic faktycznie ma inną implementację, ale ona na pewno nie sprawdza obrazów.
Sprawdza typ pliku graficznego i rozmiar strumienia - ale nie porównuje obrazu :)

Innymi słowy - mając ten sam obraz w różnych formatach graficznych, ta metoda zawsze zwróci False.
Mając inne obrazy, w tym samym formacie grafiki i z identycznym rozmiarem, metoda zwróci True.
Czy to jest porównanie obrazów? ;-)

1

Nie wiem o co autorowi dokładnie chodzi. Nie znam też wewnętrznych implementacji klas, o których rozmawiacie.
Ale padło tu pytanie jak porównać dwa pliki graficzne czy są takie same.
Jeśli chodzi o bitmapy to stosunkowo proste zadane.
Bitmapy są przechowywane w pamięci w dwóch postaciach:

  1. Albo jako zależne od sprzętu (z uchwytem HBITMAP)
  2. Albo jako bitmapy DIB (niezależne od sprzętu).

W tym drugim przypadku aby manipulować bitmapą trzeba znać dwa adresy:

  1. Adres nagłówka BITMAPINFOHEADER
  2. Adres bajtów stanowiących treść obrazu
    Jeśli bitmapa ma mniej niż 8 bitów na piksel, to bezpośrednio za BITMAPINFOHEADER pamiętana jest tablica kolorów.
    Bajty stanowiące treść obrazu mogą znajdować się bezpośrednio za tablicą kolorów, ale nie muszą.

Żeby porównać dwie bitmapy trzeba:

  1. Porównać ich nagłówki (memcmp(adrHeader1, adrHeader2, sizeof(BITMAPINFOHEADER))
    Jeśli są różne - to na pewno bitmapy są różne.

  2. Sprawdzić pole biBitCount w BITMAPINFOHEADER i jeśli jest osiem lub mniej, to wyliczyć rozmiar tablicy kolorów.
    Tablica kolorów jest pamiętana jako tablica RGBQUAD (cztery bajty na jeden kolor).
    Liczba pozycji w tablicy kolorów to 2 do potęgi biBitCount.
    Znając rozmiar tablicy kolorów, trzeba porównać tablice obu bitmap i jeśli są różne - to bitmapy na pewno są różne.
    BITMAPINFOHEADER + tablica kolorów wzięte razem stanowią strukturę BITMAPINFO.

  3. Porównać bajty zawierające treść obrazu
    Rozmiar tego obszaru - to:
    ScanBytes(header->biWidth, header->biBitCount)*header->biHeight;
    ScanBytes - to funkcja wyliczająca szerokość linii skanowania

long ScanBytes(long pixWidth, int bitsPixel) {
           return ((pixWidth*bitsPixel+31)>>5)<<2;
}

(linia skanowania w bajtach jest zaokrąglana do najbliższej granicy czterech bajtów w górę).

Jeśli natomiast bitmapa jest pamiętana jako zależna od sprzętu, to mamy do niej uchwyt HBITMAP.
Wtedy trzeba byłoby ją przekonwertować na DIB. Aby to zrobić należy przydzielić dwa obszary pamięci (na BITMAPINFO, tzn. BITMAPINFOHEADER + ewentualnie tablica kolorów i na bajty zawierające treść obrazu), a następnie wykonać funkcję GetDIBits (przed jej wykonaniem należy wypełnić BITMAPINFOHEADER). Po wykonaniu tej funkcji mamy dane w BITMAPINFOHEADER i w obszarze z treścią obrazu i dalej działamy jak opisałem wyżej.

1

Jeśli mowa o bitmapach ładowanych do obiektów klasy TBitmap, to porównanie ich zawartości jest bardzo proste. Wystarczy skorzystać z właściwości ScanLine i z faktu, że piksele są 24-bitowe w pamięci.

function CompareBitmaps(ABitmapA, ABitmapB: TBitmap): Boolean;
var
  LineIndex, LineSize: Integer;
begin
  if ABitmapA.Width <> ABitmapB.Width then Exit(False);
  if ABitmapA.Height <> ABitmapB.Height then Exit(False);

  LineSize := ABitmapA.Width * SizeOf(TRGBTriple);

  for LineIndex := 0 to ABitmapA.Height - 1 do
    if not CompareMem(ABitmapA.ScanLine[LineIndex], ABitmapB.ScanLine[LineIndex], LineSize) then
      Exit(False);

  Result := True;
end;

1 użytkowników online, w tym zalogowanych: 0, gości: 1