Path: csiph.com!tncsrv06.tnetconsulting.net!usenet.blueworldhosting.com!diablo1.usenet.blueworldhosting.com!not-for-mail From: Maria Sophia Newsgroups: alt.comp.os.windows-10,rec.photo.digital,comp.lang.python Subject: Re: PSA Using Python Pillow to foil camera image PRNU fingerprinting Date: Mon, 3 Aug 2026 09:20:43 -0800 Organization: BWH Usenet Archive (https://usenet.blueworldhosting.com) Message-ID: <114qilb$1dva$1@nnrp.usenet.blueworldhosting.com> References: <114en2t$7bu$1@nnrp.usenet.blueworldhosting.com> <114f0l7$1is0l$1@dont-email.me> <114frd8$2okp$1@nnrp.usenet.blueworldhosting.com> <114j5su$4kq$1@nnrp.usenet.blueworldhosting.com> <114j8pa$34k3s$2@dont-email.me> <114ja7g$2elo$1@nnrp.usenet.blueworldhosting.com> <114nvta$m7jf$1@dont-email.me> <114o568$rh8$1@nnrp.usenet.blueworldhosting.com> <114ohr2$rsgh$4@dont-email.me> Reply-To: pusvul@getTjewytR4so+mqe2.invalid Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Injection-Date: Mon, 3 Aug 2026 17:20:44 -0000 (UTC) Injection-Info: nnrp.usenet.blueworldhosting.com; logging-data="47082"; mail-complaints-to="usenet@blueworldhosting.com"; posting-host="0da0fb4e8763147dfb976ccaebd130ab"; posting-account="a24474141b7b153dd56a8f6e0f30f8b5"; User-Agent: telnet + stunnel + gVim on Windows Cancel-Lock: sha1:KRJD/HWADM8RinYdSFhwqvGuywc= sha256:rtstJDCkMjWeE4A+AMT1+DbKlw0J9l092dxHDGLaBxs= sha1:LAINHrl1P9/06eKxlwQ78EkzO+8= sha256:y1KmVCwGjUsV/4Qg72B4pbjwpK1q3Kqg84MogQtolD0= Content-Language: en-GB Xref: csiph.com alt.comp.os.windows-10:194894 rec.photo.digital:244702 comp.lang.python:197860 Hi Lawrence, Thanks for your advice, where I have been looking up the methods that you & Piergiorgio suggested, both of which are far better than my original idea. I liked your idea of building a PRNU fingerprint from a set of same-camera calibration images of matching resolution and subtracting that fingerprint from the target image to produce a PRNU-reduced scrubbed image. That's what the prnuwash.py script attempted to accomplish, which was a better method than the original method I had tried, since prnu.py used blind PRNU estimation techniques to suppress sensor-specific fingerprints. I think each of us adds more value to the problem set discussion, where everyone can benefit from our ideas, even those who are only lurking here. Below is a a script that reduces the real fingerprint in order to then apply a stronger fake fingerprint to implement Piergiorgio's suggestion. I had to add cv2 since it produces a more realistic PRNU overall. pip3.exe install opencv-python But I really need to also add BM3D as Piergiorgio had suggested. python fakeprnu.py Loaded: input.jpg resolution: (1067, 800, 3) Denoised image to weaken original PRNU. Generated synthetic PRNU map. Applied synthetic PRNU multiplicatively. Saved: fakeprnu.jpg # -------------------------------------------------------------------- # fakeprnu.py # A cv2-based PRNU scrubber that applies a fake fingerprint to an image. # -------------------------------------------------------------------- # This script is intended to apply a fake fingerprint onto an image file. # It's designed to hinder PRNU fingerprinting when posting images online. # It does not require calibration images like the previous prnuwash.py did. # # 1. Place the image you want to process as input.jpg # 2. Run: python fakeprnu.py # 3. Output: scrubbed.jpg # # The approach: # A. Denoise the image to weaken the original PRNU # B. Generate a synthetic fixed-pattern PRNU map # C. Apply the synthetic PRNU multiplicatively: # img_out = img_denoised * (1 + prnu_map) # # -------------------------------------------------------------------- # v1p1 20260803 reduced denoising from 10 to 5 due to visible blur effect # WIP: BM3D should be added as it reduces noise without edge blur. # v1p0 20260803 initial version implementing synthetic PRNU overlay # -------------------------------------------------------------------- import cv2 import numpy as np INPUT_IMAGE = "input.jpg" OUTPUT_IMAGE = "fakeprnu.jpg" # Step 1: Denoise image to weaken original PRNU # Note the option to skip denoicing altogether # img_denoised = img # 10 was a bit too blurry # def denoise_image(img, strength=10): def denoise_image(img, strength=5): print("Denoised image to weaken original PRNU.") # Uses OpenCV fastNlMeansDenoisingColored # This is not BM3D, but it is simple and available everywhere. return cv2.fastNlMeansDenoisingColored( img, None, h=strength, hColor=strength, templateWindowSize=7, searchWindowSize=21 ) # Step 2: Generate synthetic fixed-pattern PRNU def generate_fake_prnu(shape, amplitude=0.02, smooth_kernel=21): h, w, c = shape # Start with random noise noise = np.random.randn(h, w, c).astype(np.float32) # Smooth to create spatial correlation smooth = cv2.GaussianBlur(noise, (smooth_kernel, smooth_kernel), 0) # Normalize to zero mean, unit variance mean = np.mean(smooth) std = np.std(smooth) + 1e-8 norm = (smooth - mean) / std # Scale to desired amplitude prnu_map = amplitude * norm return prnu_map # Step 3: Apply multiplicative fake PRNU def apply_fake_prnu(img, prnu_map): img_f = img.astype(np.float32) / 255.0 out = img_f * (1.0 + prnu_map) out = np.clip(out, 0.0, 1.0) out = (out * 255.0).astype(np.uint8) return out # Main def main(): img = cv2.imread(INPUT_IMAGE, cv2.IMREAD_COLOR) if img is None: raise RuntimeError("Could not load input.jpg") print("Loaded:", INPUT_IMAGE, "resolution:", img.shape) # Step A: weaken original PRNU img_denoised = denoise_image(img) print("Denoised image to weaken original PRNU.") # Step B: synthetic PRNU fake_prnu = generate_fake_prnu(img.shape, amplitude=0.02, smooth_kernel=21) print("Generated synthetic PRNU map.") # Step C: apply multiplicative PRNU img_out = apply_fake_prnu(img_denoised, fake_prnu) print("Applied synthetic PRNU multiplicatively.") cv2.imwrite(OUTPUT_IMAGE, img_out) print("Saved:", OUTPUT_IMAGE) if __name__ == "__main__": main() # end of fakeprnu.py -- On Usenet, we all try to help each other by leveraging knowledge.