Connect to piFilter: Technical manual

General Information

piFilter is the online engine, which receives your request for each image and generates the adult content estimation for each image. piFilter responds with the estimation value equal to 'porn', 'no porn' or 'suspicious'. Then you are able to handle this result as it is needed for your business. You should develop the simple software code to access piFilter from your service.

Three simple steps to use piFilter:

  1. Subscribe
  2. Receive the customer ID string
  3. Start using piFilter. Read this page for more info.

HTTP protocol must be used to get access to piFilter. The data is encoded into multipart/form-data and sent to piFilter engine via API. The service responds by text/plain.

piFilter supports two types of requests:

  1. request, containing the image URL (URL-based request)
  2. request, containing the image itself (Image-based request)

If you use URL-based requests, you have to make sure that URL grants direct access to the image from the external query. Some images are not accessible by URLs.

You must use the ID string in any type of the request. The ID string identifies the customer. It is issued after the first subscriber registration. In addition to that, you can use your IP-s to limit the access and ensure better protection. The default method of user identification is the ID string only.

URL-based request must contain following parameters:

  1. ControlStr - ID string
  2. Url - URL of the image

Image-based request must contain following parameters:

  1. ControlStr - ID string
  2. ImageFile - image file

ImageFile transfers as it is, as binary.

The requests to the service looks like the browser-generated requests.

For instance, the form querying piFilter by URL of the image, can look as:

<form name="URLform" method="post" 
	action="http://service.pifilter.com/Url.ashx" 
	enctype="multipart/form-data">
	<input name="Url" type="text" value="" />
	<input name="ControlStr" type="text" value="" />
	<input type="submit" name="Send" value="Send" />
</form>

The form querying piFilter by the image file, can look as:

<form name="Imageform" method="post" 
	action="http://service.pifilter.com/Image.ashx" 
	enctype="multipart/form-data">
	<input type="file" name="ImageFile"/>
	<input name="ControlStr" type="text" value="" />
	<input type="submit" name="Send" value="Send" />
</form>

You can try the forms above to perform test requests to piFilter.

In response to your request, piFilter returns the text string consisting of:

  1. Porn estimation - positive integer, if the request has been processed successfully
  2. Error code - negative integer and the text with the error description, if the request has failed by some reason.

Porn estimation values:

ValueDescription
1No porn
2Porn
3Suspicious

Error codes:

ValueDescription
Billing
-10Query limit is over
-11Unknown Control String
-12Incorrect IP address
-13Bandwidth limit is exceeded
Data errors
-20Incorrect URL
-30Image is corrupt
-31Image too small
-32Image too big
Others
-40Unknown error

If you have set up IP-s to limit the access, then you can connect only from those IP-s. Otherwise the service issues the error code -12

Software Code Samples

Most of high-level languages have the standard libraries to compose, send and process HTTP requests.

Common algorithm of using such libraries is following:

  1. Make objects to compose http-requests to the HTTP service
  2. Initiate objects to establish the connection
  3. Generate the query using the available data
  4. Send the query
  5. Process the query results received from the service.

Using Perl to query piFilter

URL-based query

#!perl -w
use HTTP::Request::Common;
require LWP::UserAgent;
 
# Url of image
my $url='http://someserver.com/someimage.jpg';
 
# Create a user agent object
 $ua = LWP::UserAgent->new;
 
# Create a request, send to user agent and get a response back
 $response = $ua->request(POST 'http://service.pifilter.com/Url.ashx',
        Content_Type => 'form-data',
        Content      => 	[Url  		=> $url,
 				ControlStr	=> 'abcdefghijklmnopqrstuvwxyz0123']);
 
# Check the outcome of the response
 if ($response->is_success) 
# if request processed successfully
 {
   ($result,$error_mesg) = split /,/,$response->content;
# Check errors
   if ($result > 0)
# if no errors
   {
     print "PRating for url $url is $result.";
   }
   else
# if error
   {
     print "Error for url $url! Error code is $result, $error_mesg.";
   }
 }
# if request processed unsuccessfully
 else { $mesg=$response->status_line; print "Error executing query. $mesg"; }

Image-based query

#!perl -w
use HTTP::Request::Common;
require LWP::UserAgent;
 
# Full name of file
my $full_filename = '/somedir/somefile.jpg';
 
# Create a user agent object
 $ua = LWP::UserAgent->new;
 
# Create a request, send to user agent and get a response back
 $response = $ua->request(POST 'http://service.pifilter.com/Image.ashx',
        Content_Type => 'form-data',
        Content      =>	[ImageFile  => [$full_filename;],
 				ControlStr  => 'abcdefghijklmnopqrstuvwxyz0123']);
 
# Check the outcome of the response
 if ($response->is_success) 
# if request processed successfully
 {
   ($result,$error_mesg) = split /,/,$response->content;
# Check errors
   if ($result > 0)
# if no errors
   {
     print "PRating for url $url is $result.";
   }
   else
# if error
   {
     print "Error for url $url! Error code is $result, $error_mesg.";
   }
 }
# if request processed unsuccessfully
 else { $mesg=$response->status_line; print "Error executing query. $mesg"; }

If you have the image itself and you do not need saving it on the hard disk , you can send a direct request to API replacing

Content      => [ImageFile  => [$full_filename;],

by following lines

Content  => [ImageFile  =>
                          undef,  
                          "$session",  
                          'Content_Type' => 'image/jpeg;',
                          'Content' => $imagecontent],

Using MS Visual C# to query piFilter

Two classes made in MS .NET platform can be used to work with piFilter:

  • PFilterRequest – performs the query
  • PFilterRequestResult – represents the result of the query.

Both methods, URL-based and Image-based queries are realized by those classes.

using System;
using System.IO;
using System.Net;
using System.Text;
 
 
class Program
{
    static void Main( String[] args )
    {
        // Image Url 
        const String sampleUrl = "http://someserver.com/someimage.jpg";
 
        // Service Url
        const String serverUrl = "http://service.pifilter.com";
 
        // Control string
        const String controlString = "abcdefghijklmnopqrstuvwxyz0123";
 
        // Make query manager
        PFilterRequest request = new PFilterRequest( serverUrl, controlString );
 
        // Example #1. URL-based query
 
        // Request for estimation by the URL
        PFilterRequestResult result = request.GetPRatingByUrl( sampleUrl );
 
        // Result analysis
        if( result.ErrorCode < 0 )
        { Console.WriteLine("Error for url {0}!\r\nError code is {1}, {2}.",
                sampleUrl, result.ErrorCode, result.ErrorMessage );
        }
        else
        { Console.WriteLine( "PRating for url {0} is {1}.", sampleUrl, result.PRating ); }
 
        // Example #2. Image-based query
 
        // Load the image
        // Make the query
        WebRequest imageRequest = WebRequest.Create( sampleUrl );
        imageRequest.Method = WebRequestMethods.Http.Get;
 
        // Receives the response
        using( HttpWebResponse response = (HttpWebResponse)imageRequest.GetResponse() )
        {
            // Make local stream for the image
            using( MemoryStream localStream = new MemoryStream() )
            {
                // Receives the response stream from the service
                using( Stream stream = response.GetResponseStream() )
                {
                    // Copy the service response to the local stream
                    Byte[] buffer = new Byte[65546];
                    Int32 bytesCount;
 
                    while( (bytesCount = stream.Read( buffer, 0, buffer.Length )) > 0 )
                    { localStream.Write( buffer, 0, bytesCount ); }
                }
 
                localStream.Position = 0;
 
                // Request for estimation by the image
                result = request.GetPRatingByImage( localStream );
            }
        }
 
        // Result analysis
        if( result.ErrorCode < 0 )
        {  Console.WriteLine("Error for url {0}!\r\nError code is {1}, {2}.",
                sampleUrl, result.ErrorCode, result.ErrorMessage );}
        else
        { Console.WriteLine( "PRating for url {0} is {1}.", sampleUrl, result.PRating );}
 
        Console.ReadLine();
    }
}
 
public class PFilterRequest
{
    /// 
    /// Service Url 
    /// 
    private readonly String serverUrl;
 
    /// 
    /// ID string
    /// 
    private readonly String controlString;
 
    /// 
    /// Make query manager
    /// 
    /// service Url
    /// Control string
    public PFilterRequest( String serverUrl, String controlString )
    {   this.serverUrl = serverUrl;
        this.controlString = controlString;
    }
 
    /// 
    /// Request for estimation for this url
    /// 
    /// image Url
    /// Porn value
    public PFilterRequestResult GetPRatingByUrl( String url )
    {
        // Delimiter
        String boundary = "----" + DateTime.Now.Ticks.ToString( "x" );
        Byte[] openBoundary = Encoding.ASCII.GetBytes( "--" + boundary + "\r\n" );
 
        // Make the query header
        WebRequest request = WebRequest.Create( serverUrl + "/Url.ashx" );
        request.Method = WebRequestMethods.Http.Post;
        request.ContentType = String.Format( "multipart/form-data; boundary={0}", boundary );
        request.Timeout = 30000;
 
        // Write sections into the query stream
        using( Stream reqStream = request.GetRequestStream() )
        {
            // Initial delimiter
            reqStream.Write( openBoundary, 0, openBoundary.Length );
 
            // 1-st section. Control word
            String part1 = String.Format(
                "Content-Disposition: form-data; name=\"ControlStr\"\r\n\r\n{0}\r\n", controlString );
 
            Byte[] part1Bytes = Encoding.UTF8.GetBytes( part1 );
            reqStream.Write( part1Bytes, 0, part1Bytes.Length );
 
            // Delimiter
            reqStream.Write( openBoundary, 0, openBoundary.Length );
 
            // 2-nd section. Image Url
            String part2 = String.Format(
                "Content-Disposition: form-data; name=\"Url\"\r\n\r\n{0}\r\n", url );
 
            Byte[] part2Bytes = Encoding.UTF8.GetBytes( part2 );
            reqStream.Write( part2Bytes, 0, part2Bytes.Length );
 
            // Final delimiter
            Byte[] closeBoundary = Encoding.ASCII.GetBytes( "--" + boundary + "--" + "\r\n" );
            reqStream.Write( closeBoundary, 0, closeBoundary.Length );
        }
 
        Byte[] resBytes;
 
        // Receive service response
        using( WebResponse response = request.GetResponse() )
        {
            // Receive response stream
            using( Stream resStream = response.GetResponseStream() )
            {
                // Copy the response stream to the memory stream
                using( MemoryStream ms = new MemoryStream() )
                {   Int32 nread;
                    Byte[] buffer = new Byte[4096];
                    while( (nread = resStream.Read( buffer, 0, 4096 )) != 0 )
                    { ms.Write( buffer, 0, nread ); }
 
                    // Encode the memory stream to the byte array
                    resBytes = ms.ToArray();
                }
            }
        }
 
        // Decode the response to the string
        String resString = Encoding.UTF8.GetString( resBytes );
 
        // Split into words
        String[] results = resString.Split( ",".ToCharArray() );
 
        // No words mean the error response
        if( results.Length == 0 )
        { throw new ApplicationException( "Invalid response!" ); }
 
        // Encode the first word into the porn value
        Int32 prating = Int32.Parse( results[0] );
 
        // If value < 0, then generate the error,
        // if value >= 0, then generate the porn estimation result
        PFilterRequestResult result = prating < 0
            ? new PFilterRequestResult( prating, results[1] )
            : new PFilterRequestResult( prating );
 
        return result;
    }
 
    /// 
    /// Request for porn estimation for the image
    /// 
    /// Изображение (поток)
    /// Porn estimation value
    public PFilterRequestResult GetPRatingByImage( Stream image )
    {
        // Query string delimiter
        String boundary = "----" + DateTime.Now.Ticks.ToString( "x" );
        Byte[] openBoundary = Encoding.ASCII.GetBytes( "--" + boundary + "\r\n" );
 
        // Query header
        WebRequest request = WebRequest.Create( serverUrl + "/Image.ashx" );
        request.Method = WebRequestMethods.Http.Post;
        request.ContentType = String.Format( "multipart/form-data; boundary={0}", boundary );
        request.Timeout = 30000;
 
        // Write sections into the query string
        using( Stream reqStream = request.GetRequestStream() )
        {
            // Initial delimiter
            reqStream.Write( openBoundary, 0, openBoundary.Length );
 
            // First section. Control word
            String part1 = String.Format(
                "Content-Disposition: form-data; name=\"ControlStr\"\r\n\r\n{0}\r\n", controlString );
 
            Byte[] part1Bytes = Encoding.UTF8.GetBytes( part1 );
            reqStream.Write( part1Bytes, 0, part1Bytes.Length );
 
            // Delimiter
            reqStream.Write( openBoundary, 0, openBoundary.Length );
 
            // Second section. Image
            // Section header
            String part2 =
                "Content-Disposition: form-data; name=\"ImageFile\"; filename=\"ImageFile\"\r\n" +
                "Content-Type: application/octet-stream\r\n" +
                "Content-Transfer-Encoding: binary\r\n\r\n";
 
            Byte[] part2Bytes = Encoding.UTF8.GetBytes( part2 );
            reqStream.Write( part2Bytes, 0, part2Bytes.Length );
 
            // Image stream
            Int32 nread;
            Byte[] buffer = new Byte[4096];
            while( (nread = image.Read( buffer, 0, 4096 )) != 0 )
            { reqStream.Write( buffer, 0, nread ); }
 
            // Line feed
            reqStream.WriteByte( (Byte)'\r' );
            reqStream.WriteByte( (Byte)'\n' );
 
            // Final delimiter
            Byte[] closeBoundary = Encoding.ASCII.GetBytes( "--" + boundary + "--" + "\r\n" );
            reqStream.Write( closeBoundary, 0, closeBoundary.Length );
        }
 
        Byte[] resBytes;
 
        // Receive the server response
        using( WebResponse response = request.GetResponse() )
        {   
            // Receive the response stream
            using( Stream resStream = response.GetResponseStream() )
            {   
                // Copy the response stream to the memory
                using( MemoryStream ms = new MemoryStream() )
                {   Int32 nread;
                    Byte[] buffer = new Byte[4096];
                    while( (nread = resStream.Read( buffer, 0, 4096 )) != 0 )
                    { ms.Write( buffer, 0, nread ); }
 
                    // Encode the memory stream to the byte array
                    resBytes = ms.ToArray();
                }
            }
        }
 
        // Decode the response to the string
        String resString = Encoding.UTF8.GetString( resBytes );
 
        // Split the string to words
        String[] results = resString.Split( ",".ToCharArray() );
 
        // No words means the error
        if( results.Length == 0 )
        { throw new ApplicationException( "Invalid response!" ); }
 
        // Encode first word into the estimation value
        Int32 prating = Int32.Parse( results[0] );
 
        // If value < 0, then generate the error,
        // if value >= 0, then generate the porn estimation result
        PFilterRequestResult result = prating < 0
            ? new PFilterRequestResult( prating, results[1] )
            : new PFilterRequestResult( prating );
 
        return result;
    }
}
 
public class PFilterRequestResult
{
    /// 
    /// False value if the error occurs
    /// 
    public const Int32 ErrorPRating = -1;
 
    /// 
    /// Error code if no errors
    /// 
    public const Int32 NoErrorCode = 0;
 
    /// 
    /// Error description
    /// 
    public const String NoErrorMessage = "No error";
 
    /// 
    /// Porn estimation value
    /// 
    private readonly Int32 pRating;
 
    /// 
    /// Error code
    /// 
    private readonly Int32 errorCode;
 
    /// 
    /// Error description
    /// 
    private readonly String errorMessage;
 
    /// 
    /// Common constructor
    /// 
    /// Porn estimation value 
    /// Error code 
    /// Error description 
    public PFilterRequestResult( Int32 pRating, Int32 errorCode, String errorMessage )
    {   this.pRating = pRating;
        this.errorCode = errorCode;
        this.errorMessage = errorMessage;
    }
 
    /// 
    /// Valid result constructor
    /// 
    /// Porn estimation value 
    public PFilterRequestResult( Int32 pRating )
    {   this.pRating = pRating;
        this.errorCode = NoErrorCode;
        this.errorMessage = NoErrorMessage;
    }
 
    /// 
    /// Constructor if error occurs
    /// 
    /// Error code 
    /// Error description 
    public PFilterRequestResult( Int32 errorCode, String errorMessage )
    {   this.pRating = ErrorPRating;
        this.errorCode = errorCode;
        this.errorMessage = errorMessage;
    }
 
    /// 
    /// Porn estimation value
    /// 
    public Int32 PRating
    { get { return pRating; } }
 
    /// 
    /// Error code
    /// 
    public Int32 ErrorCode
    { get { return errorCode; } }
 
    /// 
    /// Error description
    /// 
    public String ErrorMessage
    { get { return errorMessage; } }
}