Batch Watermark with Delphi part I
As a novice photographer, always upload my pictures on flickr and the other day someone told me about putting watermarks to the pictures just in case (in order to protect my pictures). Well, said and done, here you can find one of my last applications: Thundax batch watermark. A simple application that lets you upload a set of jpeg images and a bitmap watermark and then embed the mark in each of the images by merging the pixels.
Imagine that we have this picture:
And We've designed the following watermark:
Then with my application, we can merge this pictures in an aesthetical way:
And the result:
The way to achieve this is by playing with the TJPEGImage and TBitmap classes. Here you can see the source code that I've used to implement this solution:
the function:
I hope you enjoy!. Comments are welcome.!
Imagine that we have this picture:
And We've designed the following watermark:
Then with my application, we can merge this pictures in an aesthetical way:
And the result:
The way to achieve this is by playing with the TJPEGImage and TBitmap classes. Here you can see the source code that I've used to implement this solution:
procedure BatchPictures(); var jpg: TJPEGImage; bmp: TBitmap; i: integer; sFile: string; begin for i := 0 to ListView1.Items.Count - 1 do begin if ListView1.Items[i].Checked then begin bmp := TBitmap.Create; jpg := TJPEGImage.Create; jpg.LoadFromFile(ListView1.Items[i].Caption); bmp.Assign(jpg); jpg.Destroy; bmp.PixelFormat := pf24bit; AddWatermark(bmp, SpinEdit1.Value / 100); Jpg := TJPEGImage.Create; Jpg.Assign(Bmp); sFile := ExtractFileName(ListView1.Items[i].Caption); jpg.SaveToFile(Edit1.text + '\out' + sFile); bmp.Destroy; jpg.Destroy; end; end; end;
the function:
procedure AddToImageWaterMark(bitmap: TBitmap; alpha: single); procedure ColorToRGB(iColor: TColor; var R, G, B: Byte); function HexToInt(const Value: string): Integer; begin Result := StrToInt('$' + Value); end; var s: string; begin s := inttohex(iColor, 6); R := HexToInt(AnsiRightStr(s, 2)); G := HexToInt(AnsiLeftStr(AnsiRightStr(s, 4), 2)); B := HexToInt(AnsiLeftStr(s, 2)); end; type TRGB = array[0..1023] of TRGBTriple; var waterMark: TBitmap; sourceX, sourceY, distanceX, distanceY: integer; linesDestination, linesSource: ^TRGB; R,G,B : Byte; begin waterMark := TBitmap.Create; waterMark.LoadFromFile(Edit2.text); distanceY := 0; distanceX := 0; if rightBottom.Checked then distanceY := bitmap.Height - waterMark.Height - 10 else if LeftTop.Checked then distanceY := 10 else if righttop.Checked then distanceY := 10 else if leftBottom.Checked then distanceY := bitmap.Height - waterMark.Height - 10; for sourceY := 0 to waterMark.Height - 1 do begin linesSource := waterMark.ScanLine[sourceY]; linesDestination := bitmap.ScanLine[distanceY]; if rightBottom.Checked then distanceX := bitmap.Width - waterMark.Width - 10 else if LeftTop.Checked then distanceX := 10 else if righttop.Checked then distanceX := bitmap.Width - waterMark.Width - 10 else if leftBottom.Checked then distanceX := 10; for sourceX := 0 to waterMark.Width - 1 do begin ColorToRGB(ColorBox1.Selected, R, G, B); if (linesSource[sourceX].rgbtRed = R) and (linesSource[sourceX].rgbtGreen = G) and (linesSource[sourceX].rgbtBlue = B) then begin linesDestination[distanceX].rgbtRed := linesDestination[distanceX].rgbtRed; linesDestination[distanceX].rgbtGreen := linesDestination[distanceX].rgbtGreen; linesDestination[distanceX].rgbtBlue := linesDestination[distanceX].rgbtBlue; end else begin linesDestination[distanceX].rgbtRed := trunc(linesDestination[distanceX].rgbtRed * (1 - alpha) + linesSource[sourceX].rgbtRed * alpha); linesDestination[distanceX].rgbtGreen := trunc(linesDestination[distanceX].rgbtGreen * (1 - alpha) + linesSource[sourceX].rgbtGreen * alpha); linesDestination[distanceX].rgbtBlue := trunc(linesDestination[distanceX].rgbtBlue * (1 - alpha) + linesSource[sourceX].rgbtBlue * alpha); end; inc(distanceX); end; distanceY := distanceY + 1; end; waterMark.Destroy; end; end.
I hope you enjoy!. Comments are welcome.!
Let me ask, in the else statement "if (linesSource[sourceX].rgbtRed = R) ... ", does the first begin/end block make any sense? It looks like you are updating RGBTriplets with them selves ?
ReplyDeleteWhy not just:
if not ( (linesSource[sourceX].rgbtRed = R)
and (linesSource[sourceX].rgbtGreen = G)
and(linesSource[sourceX].rgbtBlue = B) )
then begin
linesDestination[distanceX].rgbtRed := trunc(linesDestination[distanceX].rgbtRed * (1 - alpha) + linesSource[sourceX].rgbtRed * alpha);
linesDestination[distanceX].rgbtGreen := trunc(linesDestination[distanceX].rgbtGreen * (1 - alpha) + linesSource[sourceX].rgbtGreen * alpha);
linesDestination[distanceX].rgbtBlue := trunc(linesDestination[distanceX].rgbtBlue * (1 - alpha) + linesSource[sourceX].rgbtBlue * alpha);
end;
or making 3 if/else independently one for each of R,G,B :
//var PSrc,PDst: ^TRGBTriple;
for sourceX := 0 to waterMark.Width - 1 do
begin
ColorToRGB(MaskColor, R, G, B);
PSrc := linesSource[sourceX];
PDst := linesDestination[distanceX];
if(PSrc^.rgbtRed <> R)then PDst^.rgbtRed := trunc(PDst^.rgbtRed * (1 - alpha) + PSrc^.rgbtRed * alpha);
if(PSrc^.rgbtGreen <> G)then PDst^.rgbtGreen := trunc(PDst^.rgbtGreen * (1 - alpha) + PSrc^.rgbtGreen * alpha);
if(PSrc^.rgbtBlue <> B)then PDst^.rgbtBlue := trunc(PDst^.rgbtBlue * (1 - alpha) + PSrc^.rgbtBlue * alpha);
inc(distanceX);
end;
Hi Krzysztof,
DeleteYou are right, that could be refactored and improved. Your suggestion could work. I did that for a reason but I can't remember why.It must be that I assign the same colour again as I don't want to alter the colour when I get the same RGB value.
Jordi
also, ColorToRGB(MaskColor, R, G, B); happens inside of the second leve for loop, and it executes soo many times. You should call it only once per entire function, somewhere outside of any loop, don't you think?
ReplyDeleteHi Krzysztof,
DeleteThe same goes to that method. It can be moved to an outer scope.
Jordi
I asked for a file that already happens. please.
ReplyDeleteHow do I have the Centro option?
ReplyDeleteDoes the project file have the updated link? the current address 4shared this file no longer exists
ReplyDelete