gpt4 book ai didi

delphi - 比较 JPEG 图像最快的解决方案是什么? (忽略元数据,仅 "pixels")

转载 作者:行者123 更新时间:2023-12-03 15:23:50 25 4
gpt4 key购买 nike

当我搜索“JPEG”和“元数据”这两个词时,我有很多操纵元数据的答案......而这与我想要的相反......;o)

我编写了一个函数,它完全按照我想要的方式工作...(如果图像相似,并且只有元数据发生变化或没有变化,则该函数返回 True ;如果至少有一个像素发生变化,它返回False)但是,我想提高性能......

瓶颈是bmp.Assign(jpg);

function CompareImages(fnFrom, fnTo: TFileName): Boolean;
var
j1, j2: TJpegImage;
b1, b2: TBitmap;
s1, s2: TMemoryStream;
begin
Result := False;
sw1.Start;
j1 := TJpegImage.Create;
j2 := TJpegImage.Create;
sw1.Stop;
sw2.Start;
s1 := TMemoryStream.Create;
s2 := TMemoryStream.Create;
sw2.Stop;
//sw3.Start;
b1 := TBitmap.Create;
b2 := TBitmap.Create;
//sw3.Stop;
try
sw1.Start;
j1.LoadFromFile(fnFrom);
j2.LoadFromFile(fnTo);
sw1.Stop;

// the very long part...
sw3.Start;
b1.Assign(j1);
b2.Assign(j2);
sw3.Stop;


sw4.Start;
b1.SaveToStream(s1);
b2.SaveToStream(s2);
sw4.Stop;
sw2.Start;
s1.Position := 0;
s2.Position := 0;
sw2.Stop;
sw5.Start;
Result := IsIdenticalStreams(s1, s2);
sw5.Stop;
finally
// sw3.Start;
b1.Free;
b2.Free;
// sw3.Stop;
sw2.Start;
s1.Free;
s2.Free;
sw2.Stop;
sw1.Start;
j1.Free;
j2.Free;
sw1.Stop;
end;
end;

sw1、...、sw5 为 TStopWatch ,我用来识别所花费的时间。

IsIdenticalStreams 来自here .

如果我直接比较TJpegImage,流是不同的......

还有更好的编码方法吗?

问候,

W。

更新:

测试从评论中提取的一些解决方案,我与此代码具有相同的性能:

type
TMyJpeg = class(TJPEGImage)
public
function Equals(Graphic: TGraphic): Boolean; override;
end;

...

function CompareImages(fnFrom, fnTo: TFileName): Boolean;
var
j1, j2: TMyJpeg;
begin
sw1.Start;
Result := False;
j1 := TMyJpeg.Create;
j2 := TMyJpeg.Create;
try
j1.LoadFromFile(fnFrom);
j2.LoadFromFile(fnTo);
Result := j1.Bitmap.Equals(j2.Bitmap);
finally
j1.Free;
j2.Free;
end;
sw1.Stop;
end;

有什么方法可以直接访问文件中的像素数据字节(跳过元数据字节)而不进行位图转换?

最佳答案

JPEG 文件由 block 组成, block 的类型由标记标识。 chunk的结构(独立的SOI、EOI、RSTn除外):

chunk type marker (big-endian FFxx)
chunk length (big-endian word)
data (length-2 bytes)

编辑: SOS block 受另一个标记限制,而不是长度。

元数据 block 以 APPn 标记 (FFEn) 开头,带有 JFIF 标题的 APP0 (FFE0) 标记除外。

因此我们可以只读取和比较重要的 block ,而忽略 APPn block 和 COM block (正如 TLama 注意到的那样)。

示例:某些 jpeg 文件的十六进制 View : enter image description here

它以 SOI(图像开始)标记 FFD8(独立,无长度)开头,

然后是 APP0 block (FFE0),长度 = 16 字节,

然后是 APP1 block (FFE1),其中包含元数据(EXIF 数据、NIKON COOLPIX 名称等),因此我们可以忽略 9053 字节(23 5D)并检查地址 2373 处的下一个 block 标记,依此类推...

编辑:简单解析示例:

var
jp: TMemoryStream;
Marker, Len: Word;
Position: Integer;
PBA: PByteArray;

procedure ReadLenAndMovePosition;
begin
Inc(Position, 2);
Len := Swap(PWord(@PBA[Position])^);
Inc(Position, Len);
end;

begin
jp := TMemoryStream.Create;
jp.LoadFromFile('D:\3.jpg');
Position := 0;
PBA := jp.Memory;

while (Position < jp.Size - 1) do begin
Marker := Swap(PWord(@PBA[Position])^);
case Marker of
$FFD8: begin
Memo1.Lines.Add('Start Of Image');
Inc(Position, 2);
end;
$FFD9: begin
Memo1.Lines.Add('End Of Image');
Inc(Position, 2);
end;
$FFE0: begin
ReadLenAndMovePosition;
Memo1.Lines.Add(Format('JFIF Header Len: %d', [Len]));
end;
$FFE1..$FFEF, $FFFE: begin
ReadLenAndMovePosition;
Memo1.Lines.Add(Format('APPn or COM Len: %d Ignored', [Len]));
end;
$FFDA: begin
//SOS marker, data stream, ended by another marker except for RSTn
Memo1.Lines.Add(Format('SOS data stream started at %d', [Position]));
Inc(Position, 2);
while Position < jp.Size - 1 do begin
if PBA[Position] = $FF then
if not (PBA[Position + 1] in [0, $D0..$D7]) then begin
Inc(Position, 2);
Memo1.Lines.Add(Format('SOS data stream ended at %d',
[Position]));
Break;
end;
Inc(Position);
end;
end;
else begin
ReadLenAndMovePosition;
Memo1.Lines.Add(Format('Marker %x Len: %d Significant', [Marker, Len]));
end;
end;
end;
jp.Free;
end;

关于delphi - 比较 JPEG 图像最快的解决方案是什么? (忽略元数据,仅 "pixels"),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10559099/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com