Ekemini Samuel
Posted on June 27, 2024
In this tutorial, we are building a Next.js application - PicFil, that filters image uploads with PixLab's NSFW API. Users can upload an image or enter a link to the image they want to check.
When the check is complete, if the image is NSFW (Not Safe For Work), it will be displayed with a blur and a score ranging from 0 to 1. The closer the score is to 1, the higher the image is NSFW, and vice versa.
At the end of this tutorial, you will understand how to integrate PixLab APIs into your projects to enhance security and other use cases.
Let's get started!
Prerequisites
- Understanding of Javascript and Next.js
- NPM installed on your machine
- A PixLab account. Sign up here https://pixlab.io/
- Cloudinary cloud account - for uploading the images
- A Code editor - VS Code, NeoVim, or your favorite!
- The desire to learn and build! 😎
What is PixLab?
PixLab is a Machine Learning SaaS platform that offers Computer Vision and Media Processing APIs either via a straightforward HTTP RESTful API or offline SDK via the SOD Embedded CV library.
How the PixLab NSFW API Works
The PixLab NSFW API is designed to detect inappropriate content in images or video frames, such as nudity and adult material. This API is useful for developers who need to automate the process of filtering user uploads to ensure they meet community guidelines or content policies.
Key Features:
- Detection: The API analyzes the content of an image or video frame and returns an NSFW score between 0 and 1. A score closer to 1 indicates a higher likelihood of inappropriate content.
- Integration with Other Endpoints: The NSFW API can be combined with other PixLab media processing endpoints, such as blur, encrypt, or mogrify, to censor images based on their NSFW score.
- HTTP Methods: The API supports GET and POST methods, allowing flexibility in sending the image data (either as a URL or a direct file upload).
Request Parameters:
-
Required:
- img: The image URL to be analyzed (for GET requests).
- file: The image file to be uploaded (for POST requests).
- key: Your PixLab API key.
Response:
The API returns a JSON object containing:
- status: The request status (e.g., 200 for success).
- score: The NSFW score of the image.
- error: Any error messages if the request fails.
Now, let's integrate the PixLab NSFW API into the Next.js application to filter image uploads based on their NSFW score.
Step 1: Create a Next.js app
Open your terminal and run these commands to create a new directory and a Next.js app.
mkdir nsfw-filter
cd nsfw-filter
npx create-next-app@latest .
Install Axios, which we will use to make HTTP requests to the PixLab API.
Run this command:
npm install axios
Axios is used to create a toast and inform the user of the result when the image is uploaded and checked with the PixLab NSFW API.
Create a provider
folder in the app directory, then enter this code in a ToasterProvider.jsx
file.
"use client";
import { Toaster } from "react-hot-toast";
const ToasterProvider = () => {
return <Toaster/>
};
export default ToasterProvider;
Step 2: Developing the API
Next, we develop an API client to make a POST
request to the PixLab NSFW api.pixlab.io endpoint, communicate with the API, and avoid CORS errors.
It takes an image
and a key
as the required terms and returns the data. You can find this in the route.jsx file in the app
directory.
In the app directory as shown above, create an api
folder and a nsfw
folder inside it; then create a route.jsx
file and enter the code:
// app/api/nsfw/route.js
import { NextResponse } from "next/server";
import axios from "axios";
export async function POST(req) {
const { img, key } = await req.json();
try {
const response = await axios.post(
`https://api.pixlab.io/NSFW?key=${key}`,
{ img, key },
{
headers: {
"Content-Type": "application/json",
},
}
);
return NextResponse.json(response.data);
} catch (error) {
return NextResponse.json(
{
message: error.response?.data?.message || error.message,
},
{ status: error.response?.status || 500 }
);
}
}
This project setup is on the GitHub repository
Step 3: Developing the App Router
The app router houses the client side and also the server-side folder. On the server side, we created a route at /api/nsfw/route.jsx
.
This file sends a POST request to the PixLab NSFW API to prevent CORS errors.
Checking the image with PixLab API.
POST /api/nsfw
Parameter | Type | Description |
---|---|---|
img |
string |
Required.User's uploaded image |
key |
string |
Required. Key form PIXLAB's api |
Response:
- Status: 200 OK
- Body: ```json
{
"data": {
"status": "pixLab_status",
"score": "pixlab_score"
}
}
#### Example Request
```http
POST /api/nsfw
Content-Type: application/json
{
"img": "user's_image",
"key": "pxi_lab_key",
}
Step 4: Developing the Client-side of the app
Here, we create a callback function from React and set the result from the image upload.
Initializing States
We create states to manage the lifecycle of the components.
The Image state
The Loading state
States:
const [image, setImage] = useState("");
const [result, setResult] = useState(null);
const [loading, setLoading] = useState(false);
Uploading images using next-cloudinary
**next-cloudinary:**
<CldUploadWidget
multiple
onSuccess={handleUpload}
uploadPreset="dl93zl9fn"
folder="uploads"
sources={["local", "url", "camera"]} >
{({ open }) => {
return (
<div
className="w-full cursor-pointer border-dotted px-4 md:px-8 border-4 border-[rgba(0,0,0,.2)] h-[250px] flex flex-col gap-4 items-center justify-center
"
onClick={() => open()} >
<CiImageOn fontSize={"38px"} />
<span className="text-sm text-center">
Upload a fileNo file chosen or drag and drop PNG, JPG,
GIF up to 10MB
</span>
</div>
);
}}
</CldUploadWidget>
Step 5: Interacting with the Nextjs server
Here is the code we use in developing the application's logic, which will interact with the server.
// Retrieve PixLab API Key from the environment variable
const tempKey = process.env.NEXT_PUBLIC_PIXLAB_API_KEY;
const handleTextImage = async () => {
setLoading(true);
try {
const { data } = await axios.post("/api/nswft", {
img: image,
key: tempKey,
});
setResult(data);
toast.success("Image test successfull!!");
setLoading(false);
} catch (error) {
setLoading(false);
toast.error(
error.response && error.response.data.message
? error.response.data.message
: error.message
);
}
};
Setting up the Logic
We make use of ternary operators to check for conditions in the rendering of the images.
So, in the first line, we are saying that if the state (image) is empty and the result from the server is not defined, we should render the div. This div is also conditioned to check if the score response from the server is 1. If it is 1, then we should display the image without blurring it else, we should blur the image. Otherwise, we should return the image when it has not yet been sent to the server.
{image !== "" && result ? (
<div className="w-full">
{result?.score === 1 ? (
<Image
alt="Cotion"
width={0}
sizes="100vw"
height={0}
loading="lazy"
src={image}
// blurDataURL={image | ""}
className="h-[250px] w-full md:mx-auto object-cover"
/>
) : (
<Image
alt="Cotion"
width={0}
sizes="100vw"
height={0}
loading="lazy"
style={{ filter: "blur(10px" }}
src={image}
className="h-[250px] w-full md:mx-auto object-cover"
/>
)}
</div>
) : (
<>
{image ? (
<Image
alt="Cotion"
width={0}
sizes="100vw"
height={0}
loading="lazy"
src={image}
// blurDataURL={image | ""}
className="h-[250px] w-full md:mx-auto object-cover"
/>
) : (
<CldUploadWidget
multiple
onSuccess={handleUpload}
uploadPreset="dl93zl9fn"
folder="uploads"
sources={["local", "url", "camera"]}
>
{({ open }) => {
return (
<div
className="w-full cursor-pointer border-dotted px-4 md:px-8 border-4 border-[rgba(0,0,0,.2)] h-[250px] flex flex-col gap-4 items-center justify-center
"
onClick={() => open()}
>
<CiImageOn fontSize={"38px"} />
<span className="text-sm text-center">
Upload a fileNo file chosen or drag and drop PNG, JPG,
GIF up to 10MB
</span>
</div>
);
}}
</CldUploadWidget>
)}
</>
)}
Then, enter this code in the index.js
file inside the app/(Home)/_components
directory, which handles the application's UI.
return (
<div className="flex flex-col relative w-full gap-4">
{loading && <Loader />}
<div className="w-full h-[100vh] flex items-center justify-center gap-8">
<div className="w-[90%] md:w-[500px] max-w-custom_1 mx-auto flex items-start justify-center flex-col gap-8">
<h2 className="text-4xl md:text-5xl font-booking_font4 text-dark">
PicFil
<span className="text-sm block font-booking_font text-start">
NSFW image filter, powered by PixLab API
</span>
</h2>
{/* <Image */}
{image !== "" && result ? (
<div className="w-full">
{result?.score === 1 ? (
<Image
alt="Cotion"
width={0}
sizes="100vw"
height={0}
loading="lazy"
src={image}
// blurDataURL={image | ""}
className="h-[250px] w-full md:mx-auto object-cover"
/>
) : (
<Image
alt="Cotion"
width={0}
sizes="100vw"
height={0}
loading="lazy"
style={{ filter: "blur(10px" }}
src={image}
className="h-[250px] w-full md:mx-auto object-cover"
/>
)}
</div>
) : (
<>
{image ? (
<Image
alt="Cotion"
width={0}
sizes="100vw"
height={0}
loading="lazy"
src={image}
// blurDataURL={image | ""}
className="h-[250px] w-full md:mx-auto object-cover"
/>
) : (
<CldUploadWidget
multiple
onSuccess={handleUpload}
uploadPreset="dl93zl9fn"
folder="uploads"
sources={["local", "url", "camera"]}
>
{({ open }) => {
return (
<div
className="w-full cursor-pointer border-dotted px-4 md:px-8 border-4 border-[rgba(0,0,0,.2)] h-[250px] flex flex-col gap-4 items-center justify-center
"
onClick={() => open()}
>
<CiImageOn fontSize={"38px"} />
<span className="text-sm text-center">
Upload a fileNo file chosen or drag and drop PNG, JPG,
GIF up to 10MB
</span>
</div>
);
}}
</CldUploadWidget>
)}
</>
)}
{result && image !== "" && (
<div className="w-full">
<h2 className="text-2xl font-booking_font4 text-dark">
<span className="font-booking_font text-base">Your Score:</span>{" "}
<span>{result?.score}</span>
</h2>
</div>
)}
<div className="flex w-full flex-col sm:flex-row items-center gap-4 justify-between">
<input
className="h-[80px] input border w-full"
onChange={(e) => {
setResult(null);
setImage(e.target.value);
}}
type="text"
name={"image"}
value={image}
placeholder="Enter you Image Url Here"
/>
<button
disabled={image === ""}
onClick={handleTextImage}
className="w-full font-bold md:w-[250px] h-[55px] rounded-[10px] text-white btn btn-1"
>
{loading ? (
<span className="flex justify-center items-center gap-2">
<ThreeDots
height="25"
width="25"
radius="10"
color={"#fff"}
ariaLabel="three-dots-loading"
wrapperStyle={{}}
wrapperClassName=""
visible={true}
/>
Uploading
</span>
) : (
"Check Image"
)}
</button>
</div>
</div>
</div>
<div className="w-full sticky bottom-0 py-4 bg-[#000] text-sm text-white flex items-center justify-center">
© 2024 PicFil. All rights reserved.
</div>
</div>
);
};
export default MainContent;
Step 6: Setting up your PixLab account
Go to the PixLab website and enter your username and email to create an account.
In your dashboard, click on API Keys like so:
Then click on Generate API Key to get the key we will use for the Next.js application.
You will then need to select the subscription plan from the options below:
The API key and env
file
Take note of it, as we'll use it to integrate PixLab NSFW API into the web application.
In the root directory of your Next.js project, create a new file named .env.local
and enter this to define the environment variable:
NEXT_PUBLIC_PIXLAB_API_KEY=your_pixlab_api_key_here
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=your_cloudinary_name_here
Replace "your_pixlab_api_key_here" with your actual PixLab API key, and add
.env.local
to your .gitignore file
How to Run the Application
Start the development server by running:
npm run dev
The web app can be accessed in your browser with this URL
When you open the URL in your browser, you can upload an image directly from your browser's UI or enter a link (URL) to an image to test the application.
In this demo, an image is uploaded, and after clicking on Check Image, the PixLab NSFW API processes the image, blurs it and displays the score like so:
Let's try out another image:
Note the difference in the scores. As noted on the PixLab NSFW documentation, the score ranges from 0 to 1. A score closer to 1 means the photo is more likely NFSW, and vice versa for a score closer to 0.
And that's it! We have successfully integrated the PixLab NSFW API into the web application.
Images used are from Unsplash by Gabin Vallet, Brian Lawson and Redd F.
To clone the project and run it locally, open your terminal and run this command:
git clone https://github.com/Tabintel/the-picfill-app
Then run npm install
to install all the project dependencies and npm run dev
to run the web app.
Get the full source code on GitHub.
With PixLab, you can enhance your applications with intelligent user experiences and implement robust content moderation systems. Whether using Javascript, Python, or other programming languages, PixLab provides the resources for your projects.
To get started:
- Create a PixLab account
- Learn about the PixLab API, and the API Reference Guide.
- Check out the full list of API endpoints and other use cases.
Posted on June 27, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.