https://doi.org/10.5201/ipol.2012.g-tvi
Revision 2cc1be6ae636163bfaaf300aeb376dbea4f887f9 authored by Software Heritage on 01 July 2012, 00:00:00 UTC, committed by Software Heritage on 30 July 2012, 00:00:00 UTC
0 parent
Raw File
Tip revision: 2cc1be6ae636163bfaaf300aeb376dbea4f887f9 authored by Software Heritage on 01 July 2012, 00:00:00 UTC
ipol: Deposit 1195 in collection ipol
Tip revision: 2cc1be6
randmask.c
/**
 * @file randmask.c
 * @brief Generate a random inpainting mask
 * @author Pascal Getreuer <getreuer@gmail.com>
 * @author Yiqing Wang <yiqing.wang@polytechnique.edu>
 * 
 * Copyright (c) 2012, Pascal Getreuer
 * All rights reserved.
 * 
 * This program is free software: you can use, modify and/or 
 * redistribute it under the terms of the simplified BSD License. You
 * should have received a copy of this license along this program. If 
 * not, see <http://www.opensource.org/licenses/bsd-license.html>.
 */

#include <stdio.h>
#include <string.h>
#include "randmt.h"
#include "drawtext.h"
#include "imageio.h"

/** @brief Quality for writing JPEG images */
#define JPEGQUALITY                 95

#define CLAMP(X,A,B)    (((X) <= (A)) ? (A) : (((X) >= (B)) ? (B) : (X)))


void PrintHelpMessage()
{
    puts("Generate a random inpainting mask, P. Getreuer, Y. Wang, 2012\n\n"
        "Syntax: randmask <type> <input> <output>\n");
    puts("where <input> and <output> are " 
    READIMAGE_FORMATS_SUPPORTED " images.\n");
    puts("Choices for <type>\n");
    puts(
    "    dots:<r>          Random dots of radius <r>\n"
    "    scribble:<r>      Random scribble with pen of radius <r>\n"
    "    Bernoulli:<p>     Random pixel mask with masking ratio <p>\n"
    "    text              Random text\n");
    puts("Example:\n"
        "  randmask scribble:3 input.bmp mask.bmp\n");
}


int Dots(unsigned char *Mask, int Width, int Height, int PenRadius);
int Scribble(unsigned char *Mask, int Width, int Height, int PenRadius);
void RandomText(unsigned char *Mask, int Width, int Height);
int Bernoulli(unsigned char *Mask, int Width, int Height, double Ratio);

int main(int argc, char **argv)
{
    const char *Type, *InputFile, *OutputFile;
    char *ParamString;
    unsigned char *Image = NULL;
    double Param = 1;
    int Width, Height, Status = 1;
    
    if(argc != 4)
    {
        PrintHelpMessage();
        return 0;
    }
    
    /* Read command line arguments */
    if((ParamString = strchr(argv[1], ':')))
    {
        Param = atof(ParamString + 1);
	    *ParamString = '\0';		
    }
    
    if(Param < 0)
        Param = 0;
    
    Type = argv[1]; 
    InputFile = argv[2];
    OutputFile = argv[3];
    
    /* Read the input image */
    if(!(Image = (unsigned char *)ReadImage(&Width, &Height, InputFile, 
        IMAGEIO_GRAYSCALE | IMAGEIO_U8)))
        goto Catch;
    
    memset(Image, 0, ((long)Width) * ((long)Height));
    
    /* Initialize random number generator */
    init_randmt_auto();
    
    if(!strcmp(Type, "dots"))
    {
        if(!Dots(Image, Width, Height, (int)Param))
            goto Catch;
    }
    else if(!strcmp(Type, "scribble"))
    {
        if(!Scribble(Image, Width, Height, (int)Param))
            goto Catch;
    }
    else if(!strcmp(Type, "Bernoulli"))
    {   
        if(!Bernoulli(Image, Width, Height, Param))
            goto Catch;
    }
    else if(!strcmp(Type, "text"))
        RandomText(Image, Width, Height);
    else
    {
        fprintf(stderr, "Unknown mask type, \"%s\".\n", Type);
        goto Catch;
    }
    
    /* Write the mask image */
    if(!WriteImage(Image, Width, Height, OutputFile, 
        IMAGEIO_GRAYSCALE | IMAGEIO_U8, JPEGQUALITY))
    {
        fprintf(stderr, "Error writing to \"%s\".\n", OutputFile);
        goto Catch;
    }
    
    Status = 0;
Catch:
    Free(Image);
    return Status;
}


unsigned char *MakePen(int PenRadius)
{
    const int PenRadiusSquared = (int)((PenRadius + 0.5f)*(PenRadius + 0.5f));
    const int PenWidth = 2*PenRadius + 1;    
    unsigned char *Pen = NULL;    
    int x, y, i;
    
    if((Pen = calloc(PenWidth*PenWidth, sizeof(unsigned char))))
        for(y = -PenRadius, i = 0; y <= PenRadius; y++)
            for(x = -PenRadius; x <= PenRadius; x++, i++)
                if(x*x + y*y <= PenRadiusSquared)
                    Pen[i] = 1;
    
    return Pen;
}


void PutPixel(unsigned char *Image, int Width, int Height, 
    int x, int y)
{
    if(0 <= x && x < Width && 0 <= y && y < Height)
        Image[x + Width*y] = 255;
}


void PutPen(unsigned char *Image, int Width, int Height, 
    unsigned char *Pen, int PenRadius, int x0, int y0)
{
    int x, y, i;
    
    for(y = -PenRadius, i = 0; y <= PenRadius; y++)
        for(x = -PenRadius; x <= PenRadius; x++, i++)
            if(Pen[i])
                PutPixel(Image, Width, Height, x0 + x, y0 + y);
}


unsigned char GetPixel(unsigned char *Image, 
    int Width, int Height, int x, int y)
{
    if(0 <= x && x < Width && 0 <= y && y < Height)
        return Image[x + Width*y];
    else
        return 0;
}


/**
 * @brief Fill inpainting mask with random dots
 * @param Mask destination
 * @param Width, Height the mask dimensions
 * @param PenRadius the radius of the dots
 * @return 1 on success, 0 on failure
 */
int Dots(unsigned char *Mask, int Width, int Height, int PenRadius)
{
    const long NumPoints = (long)
        (2 * sqrt(((float)Width) * ((float)Height))/PenRadius + 0.5);
    unsigned char *Pen = NULL;
    long i;
    
    if(!(Pen = MakePen(PenRadius)))
        return 0;
    
    for(i = 0; i < NumPoints; i++)
        PutPen(Mask, Width, Height, Pen, PenRadius,
            (int)(Width * rand_unif()), (int)(Height * rand_unif()));
    
    Free(Pen);
    return 1;
}

/**
 * @brief Fill inpainting mask with Bernoulli distributed pixel-wise mask
 * @param Mask destination
 * @param Width, Height the mask dimensions
 * @param Ratio masking ratio: probability that a pixel is part of the mask
 */
int Bernoulli(unsigned char *Image, int Width, int Height, double Ratio)
{
    const long NumPixels = ((long)Width) * ((long)Height);
    long i;

    if(Ratio < 0 || Ratio > 1)
    {
        fprintf(stderr, "Invalid ratio %1.1f "
            "(should be in the interval [0,1]).\n", Ratio);
        return 0;
    }

    for(i = 0; i < NumPixels; i++) 
        if(rand_unif() < Ratio)
            Image[i] = 255;

    return 1;
}

/**
 * @brief Fill inpainting mask with a random scribble
 * @param Mask destination
 * @param Width, Height the mask dimensions
 * @param PenRadius the radius of the scribble line
 * @return 1 on success, 0 on failure
 * 
 * This routine generates a random path and draws it with radius PenRadius.
 * The path is generated several times and the path with the greatest variance
 * is selected.  
 */
int Scribble(unsigned char *Mask, int Width, int Height, int PenRadius)
{
    const float Accel = 0.15f;
    const float Force = 0.1f;
    const int NumPaths = 20;
    const long NumPoints = (long)
        (5 * sqrt(((float)Width) * ((float)Height)) + 0.5);
    unsigned char *Pen = NULL;
    float (*Path)[2] = NULL, (*BestPath)[2] = NULL;
    float MeanX, MeanY, Variance, BestVariance = 0;
    float PosX, PosY, VelocityX, VelocityY, VelocityMag;
    long i;
    int j, Success = 0;
    
    if(!(Pen = MakePen(PenRadius))
        || !(Path = Malloc(sizeof(float)*2*NumPoints))
        || !(BestPath = Malloc(sizeof(float)*2*NumPoints)))
        goto Catch;
    
    for(j = 0; j < NumPaths; j++)
    {
        /* Generate a random path */
        PosX = (float)(rand_unif() * Width);
        PosY = (float)(rand_unif() * Height);
        VelocityX = (float)rand_normal();
        VelocityY = (float)rand_normal();
        VelocityMag = (float)sqrt(VelocityX*VelocityX + VelocityY*VelocityY);
        VelocityX /= VelocityMag;
        VelocityY /= VelocityMag;
        
        for(i = 0; i < NumPoints; i++)
        {
            Path[i][0] = PosX;
            Path[i][1] = PosY;
            
            VelocityX += 
                Accel*((float)rand_normal() + (Width/2 - PosX)*Force/Width);
            VelocityY +=
                Accel*((float)rand_normal() + (Height/2 - PosY)*Force/Height);

            VelocityMag = (float)sqrt(VelocityX*VelocityX 
                + VelocityY*VelocityY);

            if(VelocityMag > 1)
            {
                VelocityX /= VelocityMag;
                VelocityY /= VelocityMag;
            }
            
            PosX += VelocityX;
            PosY += VelocityY;
            PosX = CLAMP(PosX, 0, Width - 1);
            PosY = CLAMP(PosY, 0, Height - 1);
            
            if((PosX <= 0 && VelocityX < 0)
                || (PosX >= Width - 1 && VelocityX > 0))
                VelocityX = 0;
            
            if((PosY <= 0 && VelocityY < 0)
                || (PosY >= Height - 1 && VelocityY > 0))
                VelocityY = 0;
        }
        
        /* Compute the variance of the path */        
        for(i = 0, MeanX = MeanY = 0; i < NumPoints; i++)
        {
            MeanX += Path[i][0];
            MeanY += Path[i][1];
        }
        
        MeanX /= NumPoints;
        MeanY /= NumPoints;
        
        for(i = 0, Variance = 0; i < NumPoints; i++)
            Variance += (Path[i][0] - MeanX)*(Path[i][0] - MeanX)
                + (Path[i][1] - MeanY)*(Path[i][1] - MeanY);
        
        if(Variance > BestVariance)
        {
            BestVariance = Variance;
            memcpy(BestPath, Path, sizeof(float)*2*NumPoints);
        }
    }
    
    for(i = 0; i < NumPoints; i++)
        PutPen(Mask, Width, Height, Pen, PenRadius,
            (int)BestPath[i][0], (int)BestPath[i][1]);
    
    Success = 1;
Catch:
    if(BestPath)
        Free(BestPath);
    if(Path)
        Free(Path);
    if(Pen)
        Free(Pen);
    return Success;
}


/**
 * @brief Fill inpainting mask with random text about TV inpainting
 * @param Mask destination
 * @param Width, Height the mask dimensions
 *
 * This routine generates a random text from several samples about TV 
 * regularized inpainting.
 */
void RandomText(unsigned char *Mask, int Width, int Height)
{
    /* Several text samples about TV inpainting */
    static const char Text0[] =
    "In the context of digital images, inpainting is used to restore regions "
    "of an image that are corrupted by noise or where the data is missing.  "
    "Inpainting is also used to solve disocclusion, to estimate the scene "
    "behind an obscuring foreground object. Inpainting is an interpolation "
    "problem, filling the unknown region with a condition to agree with the "
    "known image on the boundary.  ";
    static const char Text1[] = 
    "However, Laplace's equation is usually unsatisfactory for images since "
    "it is overly smooth.  It cannot recover a step edge passing through the "
    "region.  Total variation (TV) regularization is an effective inpainting "
    "technique which is capable of recovering sharp edges under some "
    "conditions (these conditions will be explained).  The use of TV "
    "regularization was originally developed for image denoising by Rudin, "
    "Osher, and Fatemi [3] and then applied to inpainting by Chan and Shen.  ";
    static const char Text2[] = 
    "The first variational approach to the image inpainting problem was "
    "Nitzberg and Mumford's 2.1-D sketch [2], based on a variant of the "
    "Mumford-Shah functional, and the second variational approach was the "
    "work of Masnou and Morel [6], based on solving for level lines with "
    "minimal curvature.  Bertalmio, Sapiro, Caselles, and Ballester [8] "
    "introduced the term \"image inpainting\" in analogy to artistic "
    "inpainting and proposed an anisotropic diffusion PDE model.  ";
    static const char Text3[] =
    "TV inpainting prefers straight contours as they have minimal TV, but "
    "this is less successful for recovering curved boundaries.  TV "
    "inpainting can reconstruct a stripe passing through the inpainting "
    "domain, but only if the length to be bridged is less than the stripe "
    "thickness. TV inpainting breaks the stripe if the length is greater.  "
    "TV inpainting prefers straight contours as they have minimal TV, but "
    "this is less successful for recovering curved boundaries.  ";
    const char *Text;
    int TextLength;
    char LineBuffer[1024];
    int i, j, y;
    
    for(y = -4; y < Height; y += 19)
    {
        i = (int)(4*rand_unif());
        
        if(i == 0)
            Text = Text0;
        else if(i == 1)
            Text = Text1;
        else if(i == 2)
            Text = Text2;
        else
            Text = Text3;
        
        TextLength = strlen(Text);
        i = (int)(rand_unif()*TextLength);
        j = 0;
        
        do
        {
            LineBuffer[j] = Text[i % TextLength];
            LineBuffer[j + 1] = '\0';
            i++;
            j++;
        }while(j < 1023 && TextWidth(LineBuffer) < Width + 5);
        
        /* Draw the text with an 18-point sans serif font */
        DrawText(Mask, Width, Height, -2, y, 255, LineBuffer);
    }
}

back to top