Recent

Author Topic: My hobby project (image-to-character)  (Read 1451 times)

anyone

  • Guest
Re: My hobby project (image-to-character)
« Reply #15 on: October 05, 2020, 04:43:05 pm »

Trying to open this image with IrfanView I get the message that this is a jpg file with incorrect extension. The LCL TImage (which uses the fcl-image readers/writers) loads the image without any issues.

After I renamed the panda.png to a new filename with .jpg extension, only then my program can interpret the image correctly...

anyone

  • Guest
Re: My hobby project (image-to-character)
« Reply #16 on: October 05, 2020, 04:45:41 pm »

If anyone of you remember Paint Shop Pro, there is a feature called "Increase color depth..." and "Decrease color depth...". This feature is called "color quantization" in Python programming.


Yes, I remember and I liked that feature.
IMHO, increasing color depth will be easy but decreasing is more tricky (maybe with different ways to calculate the average of the new color).
But I vote for it.

Aww... thank you for supporting my idea.

anyone

  • Guest
Re: My hobby project (image-to-character)
« Reply #17 on: October 05, 2020, 04:49:19 pm »
Besides this, I have a feature suggestion for LazPaint. I am not sure about its usefulness though.

If anyone of you remember Paint Shop Pro, there is a feature called "Increase color depth..." and "Decrease color depth...". This feature is called "color quantization" in Python programming.

As I have limited knowledge about mathematic calculation, I will leave it to you as to how to implement this feature (if you accept my suggestion).

Such as, 24-bit color reduced to 256 color or 16 color....etc.
It is possible to save a file in 256 colors, which makes color quantization. You can as well use the palette toolbar. For example generate 16 colors and then apply dithering with it. The image can still have all 24-bit colors but you can get the effect if you like.

Is there any programming API for these? It would be nice to use one instead of inventing my own.

I did not know that it can be done that way in LazPaint.

wp

  • Hero Member
  • *****
  • Posts: 7728
Re: My hobby project (image-to-character)
« Reply #18 on: October 05, 2020, 04:50:42 pm »

Trying to open this image with IrfanView I get the message that this is a jpg file with incorrect extension. The LCL TImage (which uses the fcl-image readers/writers) loads the image without any issues.

After I renamed the panda.png to a new filename with .jpg extension, only then my program can interpret the image correctly...
I guess that when you follow PascalDragon's idea with the ImageHandlers (i.e. loading the image without specifying a reader) fcl-image will open the file successfully even when it has a wrong extension.
« Last Edit: October 05, 2020, 06:29:00 pm by wp »
Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

anyone

  • Guest
Re: My hobby project (image-to-character)
« Reply #19 on: October 05, 2020, 04:59:40 pm »

Trying to open this image with IrfanView I get the message that this is a jpg file with incorrect extension. The LCL TImage (which uses the fcl-image readers/writers) loads the image without any issues.

After I renamed the panda.png to a new filename with .jpg extension, only then my program can interpret the image correctly...
I guess that when you follow PascalDragon's idea with the ImageHandlers (i.e. loading the image withoug specifying a reader) fcl-image will open the file successfully even when it has a wrong extension.

Yes, I do follow @PascalDragon's idea. The following code would not load "panda.png" successfully. (only if it was renamed as "pandapng.jpg" ,etc)

I do not know what's wrong.

Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$h+}
  2. uses fpreadgif, fpreadpng, fpreadbmp, fpreadjpeg, fpimage, classes, sysutils;
  3.  
  4. var img : TFPMemoryImage;
  5. {    Reader : TFPCustomImageReader;}
  6.     ReadFile : string;
  7.  
  8. procedure Test;
  9. var str : TStream;
  10.  
  11. begin
  12.   ReadFile := 'c:\fpc\panda.png';
  13.  
  14.   img := TFPMemoryImage.Create(0,0);
  15.   img.LoadFromFile (ReadFile);
  16.   if FileExists (ReadFile) then
  17.   begin
  18.     str := TFileStream.Create (ReadFile,fmOpenRead);
  19.     try
  20.        img.LoadFromStream (str);
  21.     finally
  22.       str.Free;
  23.     end;
  24.   end
  25.   else
  26.     WriteLn (ReadFile,' file not found.');
  27. end;
  28.  
  29. procedure Dispose;
  30. begin
  31.   {Reader.Free;}
  32.   img.Free;
  33. end;
  34.  
  35. begin
  36.   try
  37.     Test;
  38.     Dispose;
  39.   except
  40.     on e : exception do
  41.       WriteLn ('Error: ',e.message);
  42.   end;
  43. end.
  44.  

wp

  • Hero Member
  • *****
  • Posts: 7728
Re: My hobby project (image-to-character)
« Reply #20 on: October 05, 2020, 05:59:34 pm »
I see. This is because the TFPCustomImage calls a class function FindReaderFromFileName which does rely only on the extension. But internally all readers provide a function CheckContents which reads the file header; only when that is correct the reader is used.

I wrote the following function for you. It iterates through all registered readers (in ImageHandlers) and calls for each one this CheckContents function:

Code: Pascal  [Select][+][-]
  1. function GetImageReaderClass(AFileName: String): TFPCustomImageReaderClass;
  2. var
  3.   stream: TFileStream;
  4.   i: Integer;
  5.   typeName: String;
  6.   readerClass: TFPCustomImageReaderClass;
  7.   reader: TFPCustomImageReader;
  8. begin
  9.   stream := TFileStream.Create(AFileName, fmOpenRead + fmShareDenyNone);
  10.   try
  11.     for i := 0 to ImageHandlers.Count - 1 do
  12.     begin
  13.       typeName := ImageHandlers.TypeNames[i];
  14.       readerClass := ImageHandlers.ImageReader[typeName];
  15.       if Assigned(readerClass) then
  16.       begin
  17.         reader := readerClass.Create;
  18.         try
  19.           if reader.CheckContents(stream) then
  20.           begin
  21.             Result := readerClass;
  22.             exit;
  23.           end;
  24.           stream.Position := 0;
  25.         finally
  26.           reader.Free;
  27.         end;
  28.       end;
  29.     end;
  30.     Result := nil;
  31.   finally
  32.     stream.Free;
  33.   end;
  34. end;

And here is a small example how this can be applied (I only display the image size for simplicity)

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button2Click(Sender: TObject);
  2. var
  3.   rc: TFPCustomImageReaderClass;
  4.   r: TFPCustomImageReader;
  5.   img: TFPMemoryImage;
  6. begin
  7.   Label1.Caption := '';
  8.  
  9.   rc := GetImageReaderClass(fn);
  10.   if Assigned(rc) then
  11.   begin
  12.     img := TFPMemoryImage.Create(1, 1);
  13.     try
  14.       r := rc.Create;
  15.       try
  16.         img.LoadFromFile(fn, r);
  17.       finally
  18.         r.Free;
  19.       end;
  20.       Label1.Caption := Format('%d x %d', [img.Width, img.Height]);
  21.     finally
  22.       img.Free;
  23.     end;
  24.   end
  25.   else
  26.     ShowMessage('Unknwon image type.');
  27. end;
Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

wp

  • Hero Member
  • *****
  • Posts: 7728
Re: My hobby project (image-to-character)
« Reply #21 on: October 05, 2020, 06:14:39 pm »
Ah, and it's even simpler than that, because there is another class function FindHandlerFromStream which does practically the same as my GetImageReaderClass function. My sample program now can be rewritten as:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button2Click(Sender: TObject);
  2. var
  3.   stream: TFileStream;
  4.   readerClass: TFPCustomImageReaderClass;
  5.   reader: TFPCustomImageReader;
  6.   img: TFPMemoryImage;
  7. begin
  8.   Label1.Caption := '';
  9.  
  10.   stream := TFileStream.Create(FILE_NAME, fmOpenRead + fmShareDenyNone);
  11.   try
  12.     readerClass := TFPCustomImage.FindHandlerFromStream(stream).Reader;
  13.     if Assigned(readerClass) then begin
  14.       img := TFPMemoryImage.Create(0, 0);
  15.       try
  16.         reader := readerClass.Create;
  17.         try
  18.           img.LoadFromStream(stream, reader);
  19.         finally
  20.           reader.Free;
  21.         end;
  22.         Label1.Caption := Format('%d x %d', [img.Width, img.Height]);
  23.       finally
  24.         img.Free;
  25.       end;
  26.     end else
  27.       ShowMessage('Unknown image type');
  28.   finally
  29.     stream.Free;
  30.   end;
  31. end;

Here is a working version of your program:

Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$h+}
  2. uses fpreadgif, fpreadpng, fpreadbmp, fpreadjpeg, fpimage, classes, sysutils, Crt;
  3.  
  4. var img : TFPMemoryImage;
  5.     ReadFile : string;
  6.     pixel : TFPColor;
  7.     x, y:integer;
  8.     ratioX, ratioY:integer;
  9.  
  10. function SimplifyColorComponent(Value:byte):byte;
  11. begin
  12.  if Value>=52 then
  13.    SimplifyColorComponent:=63
  14.  else
  15.  if Value>=32 then
  16.    SimplifyColorComponent:=42
  17.  else
  18.  if Value>=12 then
  19.    SimplifyColorComponent:=21
  20.  else
  21.    SimplifyColorComponent:=0;
  22. end;
  23.  
  24. function DecreaseColor256(Red,Green,Blue:byte):byte;
  25. const
  26.  Palette16:array [0..15,1..3] of byte=((0,0,0),(0,0,42),(0,42,0),(0,42,42),
  27.                                        (42,0,0),(42,0,42),(42,42,0),(42,42,42),
  28.                                        (0,0,21),(0,0,63),(0,42,21),(0,42,63),
  29.                                        (42,0,21),(42,0,63),(42,42,21),(42,42,63));
  30. var
  31.  Color,Component,Value:byte;
  32.  NewRed,NewGreen,NewBlue:byte;
  33.  
  34. begin
  35.  DecreaseColor256:=0;
  36.  Component:=1;
  37.  
  38.  repeat
  39.    case Component of
  40.      1:Value:=SimplifyColorComponent(Red div 4);
  41.      2:Value:=SimplifyColorComponent(Green div 4);
  42.      3:Value:=SimplifyColorComponent(Blue div 4);
  43.    end;
  44.  
  45.    Color:=0;
  46.  
  47.    while Value<>Palette16[Color,Component] do
  48.    begin
  49.      Inc(Color);
  50.  
  51.      if Color>15 then
  52.      begin
  53.        Dec(Value,21);
  54.        Color:=0;
  55.      end;
  56.    end;
  57.  
  58.    case Component of
  59.      1:NewRed:=Value;
  60.      2:NewGreen:=Value;
  61.      3:NewBlue:=Value;
  62.    end;
  63.  
  64.    Inc(Component);
  65.  
  66.  until Component>3;
  67.  
  68.  for Color:=0 to 15 do
  69.    if (Palette16[Color,1]=NewRed) and (Palette16[Color,2]=NewGreen)
  70.    and (Palette16[Color,3]=NewBlue) then
  71.    begin
  72.      DecreaseColor256:=Color;
  73.      Exit;
  74.    end;
  75. end;
  76.  
  77. procedure Init;
  78. begin
  79.   ReadFile := ParamStr(1);
  80.   img := TFPMemoryImage.Create(0,0);
  81. end;
  82.  
  83. procedure ReadImage;
  84. var
  85.   color16, r, g, b:byte;
  86.   readerClass: TFPCustomImageReaderClass;
  87.   reader: TFPCustomImageReader;
  88.   stream: TStream;
  89. begin
  90.    if FileExists(ReadFile) then
  91.    begin
  92.      stream := TFileStream.Create(ReadFile, fmOpenRead + fmShareDenyNone);
  93.      try
  94.        stream.Position := 0;
  95.        readerClass := TFPCustomImage.FindHandlerFromStream(stream).Reader;
  96.        if Assigned(readerClass) then begin
  97.          reader := readerClass.Create;
  98.          try
  99.            img.LoadFromStream(stream, reader);
  100.          finally
  101.            reader.Free;
  102.          end;
  103.  
  104.          ratioY := img.Height div 30;
  105.          ratioX := img.Width div 90;
  106.  
  107.          y:=0;
  108.          x:=0;
  109.  
  110.          while (y<img.Height) do
  111.          begin
  112.            while (x<img.Width) do
  113.            begin
  114.              pixel := img.Colors[x,y];
  115.              r := (pixel.Red shr 8) and $00ff;
  116.              g := (pixel.Green shr 8) and $00ff;
  117.              b := (pixel.Blue shr 8) and $00ff;
  118.              color16:=DecreaseColor256(r, g, b);
  119.  
  120.              TextBackground(color16);
  121.              Write(' ');
  122.  
  123.              x:=x+ratioX;
  124.            end;
  125.  
  126.            WriteLn;
  127.            x:=0;
  128.            y:=y+ratioY;
  129.          end;
  130.        end else
  131.          WriteLn('Unknown image format.');
  132.      finally
  133.        stream.Free;
  134.      end;
  135.    end
  136.    else
  137.       WriteLn (ReadFile,' file not found.');
  138. end;
  139.  
  140. procedure Dipose;
  141. begin
  142.   Img.Free;
  143. end;
  144.  
  145. begin
  146.   if (ParamCount=0) then
  147.   begin
  148.     WriteLn('img2chr ,developed by bookhanming@outlook.my');
  149.     WriteLn;
  150.     WriteLn('img2chr <image filename>');
  151.   end
  152.   else
  153.     try
  154.       Init;
  155.       ReadImage;
  156.       Dipose;
  157.     except
  158.       on e : exception do
  159.         writeln ('Error: ',e.message);
  160.     end;
  161.  
  162.   ReadLn;
  163. end.

Please note that your original code tried to read the file twice, by ReadfromFile and then by ReadFromStream.
« Last Edit: October 05, 2020, 06:28:14 pm by wp »
Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

anyone

  • Guest
Re: My hobby project (image-to-character)
« Reply #22 on: October 05, 2020, 07:05:55 pm »
Ah, and it's even simpler than that, because there is another class function FindHandlerFromStream which does practically the same as my GetImageReaderClass function. My sample program now can be rewritten as:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button2Click(Sender: TObject);
  2. var
  3.   stream: TFileStream;
  4.   readerClass: TFPCustomImageReaderClass;
  5.   reader: TFPCustomImageReader;
  6.   img: TFPMemoryImage;
  7. begin
  8.   Label1.Caption := '';
  9.  
  10.   stream := TFileStream.Create(FILE_NAME, fmOpenRead + fmShareDenyNone);
  11.   try
  12.     readerClass := TFPCustomImage.FindHandlerFromStream(stream).Reader;
  13.     if Assigned(readerClass) then begin
  14.       img := TFPMemoryImage.Create(0, 0);
  15.       try
  16.         reader := readerClass.Create;
  17.         try
  18.           img.LoadFromStream(stream, reader);
  19.         finally
  20.           reader.Free;
  21.         end;
  22.         Label1.Caption := Format('%d x %d', [img.Width, img.Height]);
  23.       finally
  24.         img.Free;
  25.       end;
  26.     end else
  27.       ShowMessage('Unknown image type');
  28.   finally
  29.     stream.Free;
  30.   end;
  31. end;

Here is a working version of your program:

(snipped)
Please note that your original code tried to read the file twice, by ReadfromFile and then by ReadFromStream.

Oh, it is so sweet of you. Thank you. I have updated my original post accordingly (with your modified code).

Yes, I have tested with the "panda.png" (JPEG with ".png" file extension) and it works very well.

I wish to know your handle or email address (if not convenient, then that's okay) so that I can include it in my source code ("img2chr, developed by [me] and [@wp]") because "wp" is too vague for me to attribute someone.
 :)

circular

  • Hero Member
  • *****
  • Posts: 3563
    • Personal webpage
Re: My hobby project (image-to-character)
« Reply #23 on: October 05, 2020, 07:08:54 pm »
It is possible to save a file in 256 colors, which makes color quantization. You can as well use the palette toolbar. For example generate 16 colors and then apply dithering with it. The image can still have all 24-bit colors but you can get the effect if you like.

Is there any programming API for these? It would be nice to use one instead of inventing my own.

I did not know that it can be done that way in LazPaint.
Yes, all of it is in the unit BGRAColorQuantization. There is a class TBGRAColorQuantizer that handles everything for you.
Conscience is the debugger of the mind

lucamar

  • Hero Member
  • *****
  • Posts: 3186
Re: My hobby project (image-to-character)
« Reply #24 on: October 05, 2020, 07:17:13 pm »
[...] because "wp" is too vague for me to attribute someone.
 :)

Says "anyone" :D
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.10/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

anyone

  • Guest
Re: My hobby project (image-to-character)
« Reply #25 on: October 05, 2020, 07:31:12 pm »
Is there any programming API for these? It would be nice to use one instead of inventing my own.

I did not know that it can be done that way in LazPaint.
Yes, all of it is in the unit BGRAColorQuantization. There is a class TBGRAColorQuantizer that handles everything for you.

Noted with thanks.  :)

anyone

  • Guest
Re: My hobby project (image-to-character)
« Reply #26 on: October 05, 2020, 07:34:30 pm »
[...] because "wp" is too vague for me to attribute someone.
 :)

Says "anyone" :D

Haha.  :D  Actually I wanted to use "nobody" but someone has already taken that name.

wp

  • Hero Member
  • *****
  • Posts: 7728
Re: My hobby project (image-to-character)
« Reply #27 on: October 05, 2020, 07:42:00 pm »
I wish to know your handle or email address (if not convenient, then that's okay) so that I can include it in my source code ("img2chr, developed by [me] and [@wp]") because "wp" is too vague for me to attribute someone.
 :)
There is no reason to attribute me for this little piece of code. If you want you can acknowledge "contributions from the Lazarus forum" (mine was not the only one).
Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

wp

  • Hero Member
  • *****
  • Posts: 7728
Re: My hobby project (image-to-character)
« Reply #28 on: October 06, 2020, 05:03:33 pm »
Code: Pascal  [Select][+][-]
  1. procedure ReadImage;
  2. var
  3.   color16, r, g, b:byte;
  4.   readerClass: TFPCustomImageReaderClass;
  5.   reader: TFPCustomImageReader;
  6.   stream: TStream;
  7. begin
  8.    if FileExists(ReadFile) then
  9.    begin
  10.      stream := TFileStream.Create(ReadFile, fmOpenRead + fmShareDenyNone);
  11.      try
  12.        stream.Position := 0;
  13.        readerClass := TFPCustomImage.FindHandlerFromStream(stream).Reader;
  14.        if Assigned(readerClass) then begin
  15.          reader := readerClass.Create;
  16.          try
  17.            img.LoadFromStream(stream, reader);
  18.          finally
  19.            reader.Free;
  20.          end;
  21. ...

Just for completeness: the line "stream.Position := 0" highlighted in my code snippet above is not necessary (the stream position is always zero after creation anyway) and can be omitted; it is a left-over of a previous version of the code.
Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

anyone

  • Guest
Re: My hobby project (image-to-character)
« Reply #29 on: October 06, 2020, 05:37:23 pm »

Just for completeness: the line "stream.Position := 0" highlighted in my code snippet above is not necessary (the stream position is always zero after creation anyway) and can be omitted; it is a left-over of a previous version of the code.

Thanks for the update, @wp, I will remove that in my next version of source code.

 8)

 

TinyPortal © 2005-2018