Duplicate Image Finder — Exact Files

Duplicate Image Finder — Exact Files

What this tool does

Find exact duplicate image files using byte size and SHA-256 checksums. Files remain on your device.

How to use

Select 2–50 image files. Process to see groups sharing byte size and SHA-256, then download the report. The tool never deletes files.

Limitations

25 MB per file and 100 MB combined. Supported image filename extensions only; this tool hashes bytes without decoding. Different names can match. Visually identical re-encoded images or files with different metadata do not match. Similarity, face recognition and duplicate deletion are not included. SHA-256 collisions are theoretically possible.

FAQ

Are files or text uploaded?

No. Processing happens locally in your browser.

Should I review the output?

Yes. Compare it with the original using the stated limitations before use.

Does it work on mobile?

Modern browsers are supported; format support and available memory depend on your device.

`);let i=N?F(e):e;if(J===q)try{t=new u().parseFromString(i,Zt)}catch(e){}if(!t||!t.documentElement){t=rt.createDocument(J,`template`,null);try{t.documentElement.innerHTML=Ut?P:i}catch(e){}}let a=t.body||t.documentElement;return e&&r&&a.insertBefore(n.createTextNode(r),a.childNodes[0]||null),J===q?ot.call(t,U?`html`:`body`)[0]:U?t.documentElement:a},_n=function(e){let t=me?me(e):e.ownerDocument;return it.call(t||e,e,l.SHOW_ELEMENT|l.SHOW_COMMENT|l.SHOW_TEXT|l.SHOW_PROCESSING_INSTRUCTION|l.SHOW_CDATA_SECTION,null)},vn=function(e){return e=le(e,ct,` `),e=le(e,lt,` `),e=le(e,ut,` `),e},yn=function(e){var t;e.normalize();let n=me?me(e):e.ownerDocument,r=it.call(n||e,e,l.SHOW_TEXT|l.SHOW_COMMENT|l.SHOW_CDATA_SECTION|l.SHOW_PROCESSING_INSTRUCTION,null),i=r.nextNode();for(;i;)i.data=vn(i.data),i=r.nextNode();let a=(t=e.querySelectorAll)==null?void 0:t.call(e,`template`);a&&_(a,e=>{xn(e.content)&&yn(e.content)})},bn=function(e){let t=E?E(e):null;return typeof t!=`string`||Y(t)!==`form`?!1:typeof e.nodeName!=`string`||typeof e.textContent!=`string`||typeof e.removeChild!=`function`||e.attributes!==S(e)||typeof e.removeAttribute!=`function`||typeof e.removeAttributeNode!=`function`||typeof e.getAttributeNode!=`function`||typeof e.setAttribute!=`function`||typeof e.namespaceURI!=`string`||typeof e.insertBefore!=`function`||typeof e.hasChildNodes!=`function`||e.nodeType!==T(e)||e.childNodes!==y(e)},xn=function(e){if(!T||typeof e!=`object`||!e)return!1;try{return T(e)===A.documentFragment}catch(e){return!1}},Sn=function(e){if(!T||typeof e!=`object`||!e)return!1;try{return typeof T(e)==`number`}catch(e){return!1}};function Q(e,n,r){e.length!==0&&_(e,e=>{e.call(t,n,r,$t)})}let Cn=function(e,t){return!!(H&&e.hasChildNodes()&&!Sn(e.firstElementChild)&&C(Re,e.textContent)&&C(Re,e.innerHTML)||H&&e.namespaceURI===q&&He[t]&&(Sn(e.firstElementChild)||typeof e.textContent==`string`&&C(Ue[t],e.textContent))||e.nodeType===A.processingInstruction||H&&e.nodeType===A.comment&&C(ze,e.data))},wn=function(e,t){return e instanceof RegExp?C(e,t):e instanceof Function&&!!e(t,...[...arguments].slice(2))},Tn=function(e,t,n){if(!yt[t]&&An(t)&&wn(z.tagNameCheck,t))return!1;if(Pt&&!K[t]){let t=b(e),r=y(e);if(r&&t){let i=r.length;for(let a=i-1;a>=0;--a){let i=e===n?ee(r[a],!0):r[a];t.insertBefore(i,fe(e))}}}return X(e),!0},En=function(e,t,n,r){return e.length===0?t:t===n||t===r?O(t):t},$=function(e,t){return e===t||b(e)!==null?!1:(Ft&&pn(e),!0)},Dn=function(e,n){if(Q(I.beforeSanitizeElements,e,null),$(e,n))return!0;if(bn(e))return X(e),!0;let r=Y(Ye(e));if(L=En(I.uponSanitizeElement,L,_t,Et),Q(I.uponSanitizeElement,e,{tagName:r,allowedTags:L}),$(e,n))return!0;if(Cn(e,r))return X(e),!0;if(yt[r]||!(B.tagCheck instanceof Function&&B.tagCheck(r))&&!L[r]){let t=Tn(e,r,n);return t===!1&&(Q(I.afterSanitizeElements,e,null),$(e,n))?!0:t}if(j(e)===A.element&&!ln(e)||(r===`noscript`||r===`noembed`||r===`noframes`)&&C(Be,e.innerHTML))return X(e),!0;if(V&&e.nodeType===A.text){let n=vn(e.textContent);e.textContent!==n&&(ie(t.removed,{element:e.cloneNode()}),e.textContent=n)}return Q(I.afterSanitizeElements,e,null),$(e,n)},On=function(e,t,r){if(bt[t]||mn(t,e)||jt&&(t===`id`||t===`name`)&&(r in n||r in en))return!1;let i=R[t]||B.attributeCheck instanceof Function&&B.attributeCheck(t,e);return St&&C(dt,t)||xt&&C(ft,t)?!0:i?zt[t]||C(gt,le(r,mt,``))||(t===`src`||t===`xlink:href`||t===`href`)&&e!==`script`&&ue(r,`data:`)===0&&Lt[e]||Ct&&!C(pt,le(r,mt,``))?!0:!r:An(e)&&wn(z.tagNameCheck,e)&&wn(z.attributeNameCheck,t,e)||t===`is`&&z.allowCustomizedBuiltInElements&&wn(z.tagNameCheck,r)},kn=D({},[`annotation-xml`,`color-profile`,`font-face`,`font-face-format`,`font-face-name`,`font-face-src`,`font-face-uri`,`missing-glyph`]),An=function(e){return!kn[oe(e)]&&C(ht,e)},jn=function(e,t,n,r){if(N&&typeof d==`object`&&typeof d.getAttributeType==`function`&&!n)switch(d.getAttributeType(e,t)){case`TrustedHTML`:return F(r);case`TrustedScriptURL`:return et(r)}return r},Mn=function(e,t,n,r){try{return n?e.setAttributeNS(n,t,r):e.setAttribute(t,r),!bn(e)||(X(e),!1)}catch(n){return Z(t,e),!1}},Nn=function(e,n){if(Q(I.beforeSanitizeAttributes,e,null),$(e,n))return;let r=e.attributes;if(!r||bn(e))return;R=En(I.uponSanitizeAttribute,R,vt,Dt);let i={attrName:``,attrValue:``,keepAttr:!0,allowedAttributes:R,forceKeepAttr:void 0},a=r.length,o=Y(e.nodeName);for(;a--;){let n=r[a],s=n.name,c=n.namespaceURI,l=n.value,u=Y(s),d=l,f=s===`value`?d:de(d),p=!1;if(i.attrName=u,i.attrValue=f,i.keepAttr=!0,i.forceKeepAttr=void 0,Q(I.uponSanitizeAttribute,e,i),f=i.attrValue,Mt&&(u===`id`||u===`name`)&&ue(f,Nt)!==0&&(Z(s,e,n),f=Nt+f,p=!0),H&&C(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i,f)){Z(s,e,n);continue}if(u===`attributename`&&ce(f,`href`)){Z(s,e,n);continue}if(!i.forceKeepAttr){if(!i.keepAttr){Z(s,e,n);continue}if(!wt&&C(Ve,f)){Z(s,e,n);continue}if(V&&(f=vn(f)),!On(o,u,f)){Z(s,e,n);continue}f=jn(o,u,c,f),f!==d&&Mn(e,s,c,f)&&p&&re(t.removed)}}Q(I.afterSanitizeAttributes,e,null),$(e,n)},Pn=function(e){let t=null,n=_n(e);for(Q(I.beforeSanitizeShadowDOM,e,null);t=n.nextNode();)if(Q(I.uponSanitizeShadowNode,t,null),Dn(t,e),Nn(t,e),xn(t.content)&&Pn(t.content),j(t)===A.element){let e=pe(t);xn(e)&&(Fn(e),Pn(e))}Q(I.afterSanitizeShadowDOM,e,null)},Fn=function(e){let t=[{node:e,shadow:null}];for(;t.length>0;){let e=t.pop();if(e.shadow){Pn(e.shadow);continue}let n=e.node,r=j(n)===A.element,i=y(n);if(i)for(let e=i.length-1;e>=0;--e)t.push({node:i[e],shadow:null});if(r){let e=E?E(n):null;if(typeof e==`string`&&Y(e)===`template`){let e=n.content;xn(e)&&t.push({node:e,shadow:null})}}if(r){let e=pe(n);xn(e)&&t.push({node:null,shadow:e},{node:e,shadow:null})}}};return t.sanitize=function(e){let n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},i=null,a=null,o=null,s=null;if(Ut=!e,Ut&&(e=``),typeof e!=`string`&&!Sn(e)&&(e=he(e),typeof e!=`string`))throw w(`dirty is not a string, aborting`);if(!t.isSupported)return e;Tt?(L=Et,R=Dt):nn(n),(I.uponSanitizeElement.length>0||I.uponSanitizeAttribute.length>0)&&(L=O(L)),I.uponSanitizeAttribute.length>0&&(R=O(R)),t.removed=[];let c=Ft&&typeof e!=`string`&&Sn(e);if(c){hn(e);let t=Ye(e);if(typeof t==`string`){let n=Y(t);if(!L[n]||yt[n])throw dn(e),w(`root node is forbidden and cannot be sanitized in-place`)}if(bn(e))throw dn(e),w(`root node is clobbered and cannot be sanitized in-place`);try{Fn(e)}catch(t){throw dn(e),t}}else if(Sn(e))i=gn(``),a=i.ownerDocument.importNode(e,!0),a.nodeType===A.element&&a.nodeName===`BODY`||a.nodeName===`HTML`?i=a:i.appendChild(a),Fn(i);else{if(!W&&!V&&!U&&e.indexOf(`<`)===-1)return N&&At?F(e):e;if(i=gn(e),!i)return W?null:At?P:``}i&&Ot&&X(i.firstChild);let l=c?e:i;try{let e=_n(l);for(;o=e.nextNode();)Dn(o,l),Nn(o,l),xn(o.content)&&Pn(o.content)}catch(n){throw c&&(dn(e),_(t.removed,e=>{e.element&&pn(e.element)})),n}if(c){let n=!1;if(_(t.removed,t=>{t.element&&(t.element===e&&(n=!0),pn(t.element))}),n)throw w(`a node selected for removal could not be safely returned; refusing to sanitize in place`);return V&&yn(e),e}if(W){if(V&&yn(i),kt)for(s=at.call(i.ownerDocument);i.firstChild;)s.appendChild(i.firstChild);else s=i;return(R.shadowroot||R.shadowrootmode)&&(s=st.call(r,s,!0)),s}let u=U?i.outerHTML:i.innerHTML;return U&&L[`!doctype`]&&i.ownerDocument&&i.ownerDocument.doctype&&i.ownerDocument.doctype.name&&C(Ie,i.ownerDocument.doctype.name)&&(u=` `+u),V&&(u=vn(u)),N&&At?F(u):u},t.setConfig=function(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};nn(e),Tt=!0,Et=L,Dt=R},t.clearConfig=function(){$t=null,Tt=!1,Et=null,Dt=null,N=Xe,P=``},t.isValidAttribute=function(e,t,n){$t||nn({});let r=Y(e),i=Y(t);return On(r,i,n)},t.addHook=function(e,t){typeof t==`function`&&x(I,e)&&ie(I[e],t)},t.removeHook=function(e,t){if(x(I,e)){if(t!==void 0){let n=ne(I[e],t);return n===-1?void 0:ae(I[e],n,1)[0]}return re(I[e])}},t.removeHooks=function(e){x(I,e)&&(I[e]=[])},t.removeAllHooks=function(){I=Ke()},t}return Je()}); //# sourceMappingURL=purify.min.js.map (function(g){'use strict'; function limit(s){if(s.length>200000)throw Error('Text limit: 200,000 characters.');return s.replace(/\r\n?/g,'\n');} function chat(s,o={}){s=limit(s);let removed=0;const pattern=/^(?:\[\d{1,4}[/.\-]\d{1,2}[/.\-]\d{1,4},?\s+\d{1,2}:\d{2}(?::\d{2})?(?:\s*[AaPp][Mm])?\]\s*|\d{1,4}[/.\-]\d{1,2}[/.\-]\d{1,4},?\s+\d{1,2}:\d{2}(?::\d{2})?(?:\s*[AaPp][Mm])?\s*[-–]\s*)([^:\n]{1,100}):\s?/; let text=s.split('\n').map(line=>{let m=line.replace(/^[\u200e\u200f]/,'').match(pattern);if(m){removed++;line=(o.sender?m[1]+': ':'')+line.replace(/^[\u200e\u200f]/,'').slice(m[0].length);}if(o.format)line=line.replace(/([*_~])([^\n]+?)\1/g,'$2');return line;}).join('\n');return {text,removed};} function csv(s,input,output,safe=false){s=limit(s).replace(/^\ufeff/,'');if(![',',';','\t','|'].includes(input)||![',',';','\t','|'].includes(output))throw Error('Choose a supported separator.');let p=g.Papa.parse(s,{delimiter:input,dynamicTyping:false,skipEmptyLines:false});if(p.errors.length)throw Error('CSV parse error: '+p.errors[0].message);if(p.data.length>10000)throw Error('CSV limit: 10,000 rows.');return {text:g.Papa.unparse(p.data,{delimiter:output,newline:'\r\n',escapeFormulae:safe}),rows:p.data.length};} function xml(s){s=limit(s);if(/10000||depth>100)throw Error('XML structure is too large/deep.');if(n.nodeType!==1)return ser.serializeToString(n);let kids=Array.from(n.childNodes);let mixed=preserve||n.getAttribute('xml:space')==='preserve'||kids.some(k=>(k.nodeType===3&&k.nodeValue.trim())||k.nodeType===4);if(mixed||!kids.some(k=>k.nodeType===1))return ser.serializeToString(n);let copy=n.cloneNode(false),shell=ser.serializeToString(copy),open=shell.replace(/\/>$/,'>'),close='';return open+'\n'+kids.filter(k=>k.nodeType!==3||k.nodeValue.trim()).map(k=>' '.repeat(depth+1)+walk(k,depth+1)).join('\n')+'\n'+' '.repeat(depth)+close;} let text=Array.from(doc.childNodes).map(n=>walk(n,0)).join('\n');if(/^\s*<\?xml/.test(s))text=(s.match(/^\s*(<\?xml[^?]*\?>)/)||[])[1]+'\n'+text;return {text};} function html(s,comments=false){s=limit(s);let text=g.DOMPurify.sanitize(s,{USE_PROFILES:{html:true},FORBID_TAGS:['style','iframe','object','embed','form','input','button','link','meta'],FORBID_ATTR:['style'],ALLOW_DATA_ATTR:false});let removed=g.DOMPurify.removed.length;const doc=new g.DOMParser().parseFromString(text,'text/html');if(comments){const w=doc.createTreeWalker(doc.body,128);let nodes=[];while(w.nextNode())nodes.push(w.currentNode);nodes.forEach(n=>n.remove());}const block=new Set(['DIV','P','UL','OL','LI','TABLE','THEAD','TBODY','TR','SECTION','ARTICLE','H1','H2','H3','H4','BLOCKQUOTE']);function fmt(n,d){if(n.nodeType!==1)return n.textContent;let kids=Array.from(n.childNodes);if(!block.has(n.tagName)||kids.some(k=>k.nodeType===3&&k.textContent.trim())||!kids.some(k=>k.nodeType===1))return n.outerHTML;let shell=n.cloneNode(false).outerHTML;let ix=shell.indexOf('>');return shell.slice(0,ix+1)+'\n'+kids.filter(k=>k.nodeType!==3||k.textContent.trim()).map(k=>' '.repeat(d+1)+fmt(k,d+1)).join('\n')+'\n'+' '.repeat(d)+shell.slice(ix+1);} // Formatted output is text only. Sanitize once more after serialization. text=Array.from(doc.body.childNodes).map(n=>fmt(n,0)).join('\n');text=g.DOMPurify.sanitize(text,{USE_PROFILES:{html:true},FORBID_TAGS:['style','iframe','object','embed','form','input','button','link','meta'],FORBID_ATTR:['style'],ALLOW_DATA_ATTR:false});return {text,removed};} async function decode(file){if(file.size>25*1024*1024)throw Error('Image limit: 25 MB.');if(!/\.(png|jpe?g|webp|gif|avif|bmp)$/i.test(file.name))throw Error('Supported files: JPG, PNG, WebP, GIF, AVIF, BMP.');let b=await g.createImageBitmap(file,{imageOrientation:'from-image'});if(b.width*b.height>40000000){b.close();throw Error('Image limit: 40 megapixels.');}return b;} async function check(file){let b=await decode(file);let out={name:file.name,size:file.size,width:b.width,height:b.height,status:'Decoded successfully — not a full integrity or malware check'};b.close();return out;} async function duplicates(files){if(files.length<2||files.length>50)throw Error('Choose 2–50 files.');if(files.reduce((n,f)=>n+f.size,0)>100*1024*1024)throw Error('Combined file limit: 100 MB.');const groups=new Map();for(const f of files){if(!/\.(png|jpe?g|webp|gif|avif|bmp)$/i.test(f.name))throw Error('Choose supported image file names.');if(f.size>25*1024*1024)throw Error('Each file must be at most 25 MB.');let bytes=await f.arrayBuffer(),hash=Array.from(new Uint8Array(await g.crypto.subtle.digest('SHA-256',bytes)),x=>x.toString(16).padStart(2,'0')).join('');let key=f.size+':'+hash;if(!groups.has(key))groups.set(key,[]);groups.get(key).push({name:f.name,size:f.size,hash});}return Array.from(groups.values()).filter(a=>a.length>1);} async function orient(file,angle,flip,mime){angle=Number(angle);if(![0,90,180,270].includes(angle)||!['none','horizontal','vertical'].includes(flip)||!['image/png','image/jpeg','image/webp'].includes(mime))throw Error('Invalid transform.');let b=await decode(file);const c=g.document.createElement('canvas'),swap=angle%180!==0;c.width=swap?b.height:b.width;c.height=swap?b.width:b.height;const ctx=c.getContext('2d');if(mime==='image/jpeg'){ctx.fillStyle='#fff';ctx.fillRect(0,0,c.width,c.height);}ctx.translate(c.width/2,c.height/2);ctx.rotate(angle*Math.PI/180);ctx.scale(flip==='horizontal'?-1:1,flip==='vertical'?-1:1);ctx.drawImage(b,-b.width/2,-b.height/2);b.close();const blob=await new Promise(r=>c.toBlob(r,mime,.92));if(!blob||blob.type!==mime)throw Error('Browser could not export this format.');return {blob,width:c.width,height:c.height};} g.VHBatch3={chat,csv,xml,html,check,duplicates,orient};})(typeof window==='undefined'?globalThis:window); (function(){'use strict';for(const root of document.querySelectorAll('[data-vh-b3]')){if(root.dataset.ready)continue;root.dataset.ready='1';const type=root.dataset.vhB3,hi=root.dataset.lang==='hi',t=(e,h)=>hi?h:e,K=window.VHBatch3;let text=['chat','csv','xml','html'].includes(type),fields='';const label=(title,content)=>'';if(text)fields+=label(t('Input text','टेक्स्ट पेस्ट करें'),'');else fields+=label(t(type==='duplicates'?'Choose 2–50 image files':'Choose image','चित्र चुनें'),'');if(type==='chat')fields+=label(t('Keep sender names','भेजने वाले का नाम रखें'),'')+label(t('Remove paired *, _ and ~ formatting','*, _ और ~ की जोड़ी हटाएं'),'');if(type==='csv'){let opts='';fields+=label(t('Input separator','मूल separator'),'')+label(t('Output separator','आउटपुट separator'),'')+label(t('Protect spreadsheet formula cells (changes values)','Spreadsheet formula वाले cells सुरक्षित करें (values बदलेंगी)'),'');}if(type==='orient')fields+=label(t('Clockwise rotation','घड़ी की दिशा में घुमाएं'),'')+label(t('Flip before rotation','घुमाने से पहले flip'),'')+label(t('Output format','आउटपुट फॉर्मेट'),'');root.innerHTML='
'+fields+'

';const form=root.querySelector('form'),status=root.querySelector('[role=status]'),result=root.querySelector('.result');let urls=[],output='';function clear(){urls.forEach(URL.revokeObjectURL);urls=[];result.replaceChildren();output='';status.textContent='';}form.addEventListener('input',clear);function dl(blob,name){let url=URL.createObjectURL(blob);urls.push(url);let a=document.createElement('a');a.href=url;a.download=name;a.className='download';a.textContent=t('Download output','आउटपुट डाउनलोड करें');result.append(a);return url;}function show(s){output=s;const box=document.createElement('textarea');box.readOnly=true;box.rows=10;box.value=s;box.setAttribute('aria-label',t('Output','आउटपुट'));result.append(box);let b=document.createElement('button');b.type='button';b.textContent=t('Copy output','आउटपुट कॉपी करें');b.onclick=async()=>{try{await navigator.clipboard.writeText(output);status.textContent=t('Copied.','कॉपी हुआ।');}catch(e){status.textContent=t('Select the output and copy manually.','आउटपुट चुनकर कॉपी करें।');}};result.append(b);} form.addEventListener('submit',async e=>{e.preventDefault();clear();form.querySelector('button').disabled=true;status.textContent=t('Processing…','तैयार हो रहा है…');try{let o,ext='txt',mime='text/plain;charset=utf-8';if(type==='chat'){o=K.chat(form.elements.text.value,{sender:form.elements.sender.checked,format:form.elements.format.checked});status.textContent=t('Recognized message headers: ','पहचाने गए message headers: ')+o.removed;}if(type==='csv'){o=K.csv(form.elements.text.value,form.elements.input.value==='tab'?'\t':form.elements.input.value,form.elements.output.value==='tab'?'\t':form.elements.output.value,form.elements.safe.checked);ext=form.elements.output.value==='tab'?'tsv':'csv';mime='text/csv;charset=utf-8';status.textContent=t('Rows: ','Rows: ')+o.rows;}if(type==='xml'){o=K.xml(form.elements.text.value);ext='xml';status.textContent=t('Valid XML.','XML सही है।');}if(type==='html'){o=K.html(form.elements.text.value,true);ext='html';status.textContent=t('Cleaned HTML. Removed items: ','HTML साफ हुआ। हटाए items: ')+o.removed;}if(o){show(o.text);dl(new Blob([o.text],{type:mime}),'vh-'+type+'.'+ext);}else{let files=Array.from(form.elements.file.files);if(type==='check'){o=await K.check(files[0]);show(JSON.stringify(o,null,2));status.textContent=t('Browser decoded this image.','Browser ने चित्र खोल लिया।');dl(new Blob([output],{type:mime}),'image-check.txt');}if(type==='duplicates'){let groups=await K.duplicates(files);show(groups.length?JSON.stringify(groups,null,2):t('No exact file duplicates found.','बिल्कुल समान फाइलें नहीं मिलीं।'));status.textContent=t('Duplicate groups: ','समान फाइलों के समूह: ')+groups.length;dl(new Blob([output],{type:mime}),'duplicate-images.txt');}if(type==='orient'){o=await K.orient(files[0],form.elements.angle.value,form.elements.flip.value,form.elements.mime.value);const url=dl(o.blob,'oriented.'+(o.blob.type==='image/jpeg'?'jpg':o.blob.type.split('/')[1]));const img=document.createElement('img');img.src=url;img.alt=t('Transformed output preview','घुमाए चित्र का preview');img.style.maxWidth='100%';result.prepend(img);status.textContent=o.width+' × '+o.height+' px; '+o.blob.type;}}}catch(e){status.textContent=t('Error: ','त्रुटि: ')+e.message;}finally{form.querySelector('button').disabled=false;}});}})();