Please bookmark this page to avoid losing your image tool!

Image Petzval Lens Swirly Bokeh Effect Creator

(Free & Supports Bulk Upload)

Drag & drop your images here or

The result will appear here...
You can edit the below JavaScript code to customize the image tool.
async function processImage(originalImg,
    sharpnessRadiusRatioStr = "0.2",
    maxBlurPixelsStr = "10",
    swirlIntensityStr = "1.5",
    swirlStartRatioStr = "0.25",
    blurSamplesStr = "9") {

    const sharpnessRadiusRatio = parseFloat(sharpnessRadiusRatioStr);
    const maxBlurPixels = parseInt(maxBlurPixelsStr, 10);
    const swirlIntensity = parseFloat(swirlIntensityStr);
    const swirlStartRatio = parseFloat(swirlStartRatioStr);
    const numBlurSamples = Math.max(1, parseInt(blurSamplesStr, 10)); // Ensure at least 1 sample

    const outputCanvas = document.createElement('canvas');
    const ctx = outputCanvas.getContext('2d');

    const imgWidth = originalImg.naturalWidth || originalImg.width;
    const imgHeight = originalImg.naturalHeight || originalImg.height;

    outputCanvas.width = imgWidth;
    outputCanvas.height = imgHeight;

    // Draw the original image to a source canvas to get its ImageData
    const sourceCanvas = document.createElement('canvas');
    sourceCanvas.width = imgWidth;
    sourceCanvas.height = imgHeight;
    const sourceCtx = sourceCanvas.getContext('2d', { willReadFrequently: true }); // Optimization hint
    sourceCtx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
    
    let sourceImageData;
    try {
        sourceImageData = sourceCtx.getImageData(0, 0, imgWidth, imgHeight);
    } catch (e) {
        console.error("Error getting ImageData (possibly due to CORS):", e);
        // Fallback: return the original image drawn on the canvas
        ctx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
        return outputCanvas;
    }
    
    const outputImageData = ctx.createImageData(imgWidth, imgHeight);

    const centerX = imgWidth / 2;
    const centerY = imgHeight / 2;
    const halfDiagonal = Math.sqrt(centerX * centerX + centerY * centerY);

    if (imgWidth === 0 || imgHeight === 0) { // Handle zero-size images
        return outputCanvas; // Return empty canvas
    }
    
    // If halfDiagonal is 0 (e.g. 1x1 image), some calculations might fail.
    if (halfDiagonal < 1) {
        outputImageData.data.set(sourceImageData.data);
        ctx.putImageData(outputImageData, 0, 0);
        return outputCanvas;
    }

    const sharpnessPixelRadius = sharpnessRadiusRatio * halfDiagonal;
    const swirlStartPixelRadius = swirlStartRatio * halfDiagonal;

    // Helper function to get pixel value from ImageData, with boundary clamping.
    // ix, iy are integer coordinates.
    function getPixelValue(imgData, ix, iy) {
        const w = imgData.width;
        const h = imgData.height;
        const clampedX = Math.max(0, Math.min(ix, w - 1));
        const clampedY = Math.max(0, Math.min(iy, h - 1));

        const pixelIndex = (clampedY * w + clampedX) * 4;
        return [
            imgData.data[pixelIndex],
            imgData.data[pixelIndex + 1],
            imgData.data[pixelIndex + 2],
            imgData.data[pixelIndex + 3]
        ];
    }
    
    // Helper function for bilinear interpolation. x, y are float coordinates.
    function getPixelBilinear(imgData, x, y) {
        const x1 = Math.floor(x);
        const y1 = Math.floor(y);
        const x2 = x1 + 1;
        const y2 = y1 + 1;

        const Q11 = getPixelValue(imgData, x1, y1); 
        const Q21 = getPixelValue(imgData, x2, y1); 
        const Q12 = getPixelValue(imgData, x1, y2); 
        const Q22 = getPixelValue(imgData, x2, y2); 

        const fx = x - x1; 
        const fy = y - y1; 
        const fx1 = 1 - fx;
        const fy1 = 1 - fy;

        const r = Q11[0] * fx1 * fy1 + Q21[0] * fx * fy1 + Q12[0] * fx1 * fy + Q22[0] * fx * fy;
        const g = Q11[1] * fx1 * fy1 + Q21[1] * fx * fy1 + Q12[1] * fx1 * fy + Q22[1] * fx * fy;
        const b = Q11[2] * fx1 * fy1 + Q21[2] * fx * fy1 + Q12[2] * fx1 * fy + Q22[2] * fx * fy;
        const a = Q11[3] * fx1 * fy1 + Q21[3] * fx * fy1 + Q12[3] * fx1 * fy + Q22[3] * fx * fy;

        return [r, g, b, a]; // Return raw float values
    }

    function setPixel(imgData, x, y, r, g, b, a) {
        const i = (y * imgData.width + x) * 4;
        imgData.data[i] = Math.round(r);
        imgData.data[i+1] = Math.round(g);
        imgData.data[i+2] = Math.round(b);
        imgData.data[i+3] = Math.round(a);
    }

    const normSwirlDenominator = Math.max(1, halfDiagonal - swirlStartPixelRadius);
    const normBlurDenominator = Math.max(1, halfDiagonal - sharpnessPixelRadius);

    for (let y = 0; y < imgHeight; y++) {
        for (let x = 0; x < imgWidth; x++) {
            const dx = x - centerX;
            const dy = y - centerY;
            const distance = Math.sqrt(dx * dx + dy * dy);
            const angle = Math.atan2(dy, dx);

            let currentSwirl = 0;
            if (distance > swirlStartPixelRadius) {
                const normalizedDistForSwirl = Math.max(0, (distance - swirlStartPixelRadius) / normSwirlDenominator);
                currentSwirl = swirlIntensity * Math.PI * 0.5 * Math.pow(normalizedDistForSwirl, 2);
            }
            const swirledAngle = angle + currentSwirl;

            let currentBlurRadius = 0;
            if (distance > sharpnessPixelRadius) {
                const normalizedDistForBlur = Math.max(0, (distance - sharpnessPixelRadius) / normBlurDenominator);
                currentBlurRadius = maxBlurPixels * Math.pow(normalizedDistForBlur, 1.5);
            }
            currentBlurRadius = Math.min(currentBlurRadius, maxBlurPixels);

            const srcCentralX = centerX + distance * Math.cos(swirledAngle);
            const srcCentralY = centerY + distance * Math.sin(swirledAngle);

            if (currentBlurRadius < 0.5 || numBlurSamples_actual <= 1) {
                const [r, g, b, a] = getPixelBilinear(sourceImageData, srcCentralX, srcCentralY);
                setPixel(outputImageData, x, y, r, g, b, a);
            } else {
                let r_sum = 0, g_sum = 0, b_sum = 0, a_sum = 0;
                
                const [cr, cg, cb, ca] = getPixelBilinear(sourceImageData, srcCentralX, srcCentralY);
                r_sum += cr; g_sum += cg; b_sum += cb; a_sum += ca;

                // numBlurSamples includes the central sample.
                // So, loop numBlurSamples - 1 times for additional random samples.
                const numAdditionalSamples = numBlurSamples -1;

                for (let i = 0; i < numAdditionalSamples; i++) {
                    const randAngle = Math.random() * 2 * Math.PI;
                    const randRadius = Math.sqrt(Math.random()) * currentBlurRadius; 
                    
                    const offsetX = randRadius * Math.cos(randAngle);
                    const offsetY = randRadius * Math.sin(randAngle);

                    const sampleX = srcCentralX + offsetX;
                    const sampleY = srcCentralY + offsetY;

                    const [pr, pg, pb, pa] = getPixelBilinear(sourceImageData, sampleX, sampleY);
                    r_sum += pr; g_sum += pg; b_sum += pb; a_sum += pa;
                }
                setPixel(outputImageData, x, y,
                         r_sum / numBlurSamples,
                         g_sum / numBlurSamples,
                         b_sum / numBlurSamples,
                         a_sum / numBlurSamples);
            }
        }
    }

    ctx.putImageData(outputImageData, 0, 0);
    return outputCanvas;
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

The Image Petzval Lens Swirly Bokeh Effect Creator is a tool designed to apply a unique swirly bokeh effect to images, mimicking the aesthetic of Petzval lenses. By adjusting parameters like sharpness, blur intensity, and swirl characteristics, users can create visually striking images suitable for artistic photography, digital artwork, or creative projects. This tool can be particularly useful for photographers looking to enhance their portraits or landscapes with a soft, swirling background that draws attention to the subject, making it ideal for social media posts, prints, and graphic design.

Leave a Reply

Your email address will not be published. Required fields are marked *

Other Image Tools:

Image Mimiya 645 Medium Format Filter Effect Tool

Photo Fujifilm Klasse W Filter Effect Application

Image Deardorff Large Format Filter Effect Application

Image Lomo LC-A Filter Effect Tool

Image Large Format Filter Effect Application

Image Zone Plate Lens Effect Creator

Photo Kodak Retina Filter Effect Tool

Image Polaroid 600 Filter Effect Tool

Photo Black and White Yellow Filter Effect Tool

Image Contax G2 Film Camera Render Effect Applicator

Image 110 Film Format Filter Effect Tool

Photo Jupiter-9 Portrait Lens Filter Effect

Image Fujifilm GW690 Texas Leica Filter Effect Application

Image Zeiss T* Coating Filter Effect Tool

Image Hoya R72 Infrared Filter Effect Tool

Image Filter Effect for Zeiss Ikon Contaflex

Photo Olympus Mju-II/Stylus Epic Filter Effect Tool

Image NiSi Nano IR ND Filter Effect Tool

Image Polaroid SX-70 Filter Effect Tool

Image Linhof Technika Filter Effect Tool

Image Lee Big Stopper 10-Stop ND Filter Effect Tool

Image Minolta X-700 Film Camera Render Effect Creator

Image ORWO UN54 Motion Picture Film Effect Applicator

Image Shen-Hao Large Format Filter Effect Tool

Image Impossible Project Polaroid Filter Effect Tool

Photo Foma Retropan 320 Film Filter Effect Tool

Image Fuji QuickSnap Disposable Filter Effect Application

Image 220 Film Format Filter Effect

Image Black and White with Green #61 Filter Effect Tool

Image 35mm Panoramic Camera Filter Effect Tool

Image Hitech Firecrest ND Filter Effect Formatter

Photo Rodenstock Digital Vario ND Filter Effect Tool

Image Leica Yellow Filter Effect Application

Image Argus C3 Vintage Camera Filter Effect

Image ORWO NP22 Film Filter Effect Application

Image Wratten #25 Red Filter Effect Tool

See All →