The ‘Not Safe For Work’ (NSFW) model analyzes images and videos and returns probability scores on the likelihood that the image or video contains pornography. For those of you who don’t know what NSFW is, we’re talking about Not Safe For Work. R-rated content. Stuff that would probably get you fired if you were looking at it at work.
I was reading “How To Use Clarifai To Protect Your Eyes From Seeing Something They Can’t Unsee” and thought of writing a Go program instead of Python, as used in that blog post.
To write our Go program, we shall build upon what we learned in my previous article “Using Go with an image and video recognition API from Clarifai”.
Models
When images or videos are run through the tag endpoint, they are tagged using a model. A model is a trained classifier that can recognize what’s inside an image or video according to what it ‘knows’. Different models are trained to ‘know’ different things. Running an image or video through different models can produce drastically different results.
If you’d like to get tags for an image or video using a different model, you can do so by passing in a model parameter. If you omit this parameter, the API will use the default model for your application.
NSFW
The ‘Not Safe For Work’ model analyzes images and videos and returns probability scores on the likelihood that the image or video contains pornography.
The response for NSFW returns probabilities for nsfw (Not Safe For Work) and sfw (Safe For Work) that sum to 1.0. Generally, if the nsfw probability is less than 0.15, it is most likely Safe For Work. If the nsfw probability is greater than 0.85, it is most likely Not Safe For Work.
We shall be using the Model: nsfw-v1.0 and the following image available at the url — https://samples.clarifai.com/nsfw.jpg:
Here’s the complete code for our Go program “mynsfw.go”.
package main import ( "encoding/json" "fmt" "net/http" "net/url" "strings" ) const ( clientID = "Your ClientID" clientSecret = "Your Client Secret" ) type TokenResp struct { AccessToken string `json:"access_token"` ExpiresIn int `json:"expires_in"` Scope string `json:"scope"` TokenType string `json:"token_type"` } type TagResp struct { StatusCode string `json:"status_code"` StatusMsg string `json:"status_msg"` Meta struct { Tag struct { Timestamp float64 `json:"timestamp"` Model string `json:"model"` Config string `json:"config"` } `json:"tag"` } `json:"meta"` Results []struct { Docid uint64 `json:"docid"` URL string `json:"url"` StatusCode string `json:"status_code"` StatusMsg string `json:"status_msg"` LocalID string `json:"local_id"` Result struct { Tag struct { ConceptIds []string `json:"concept_ids"` Classes []string `json:"classes"` Probs []float64 `json:"probs"` } `json:"tag"` } `json:"result"` DocidStr string `json:"docid_str"` } `json:"results"` } type NSFWResp struct { StatusCode string `json:"status_code"` StatusMsg string `json:"status_msg"` Meta struct { Tag struct { Timestamp float64 `json:"timestamp"` Model string `json:"model"` Config string `json:"config"` } `json:"tag"` } `json:"meta"` Results []struct { Docid float64 `json:"docid"` URL string `json:"url"` StatusCode string `json:"status_code"` StatusMsg string `json:"status_msg"` LocalID string `json:"local_id"` Result struct { Tag struct { Classes []string `json:"classes"` Probs []float64 `json:"probs"` } `json:"tag"` } `json:"result"` DocidStr string `json:"docid_str"` } `json:"results"` } func main() { accessToken, err := requestAccessToken() if err != nil { fmt.Println(err) } nsfw, err := getNSFW(accessToken) if err != nil { fmt.Println(err) } if nsfw[0] > nsfw[1] { fmt.Println("Safe for work, you can trust Bob again (for now)!") } else { fmt.Println("Not safe for work. Classic Bob.") } } func requestAccessToken() (string, error) { form := url.Values{} form.Set("grant_type", "client_credentials") form.Set("client_id", clientID) form.Set("client_secret", clientSecret) formData := strings.NewReader(form.Encode()) url := fmt.Sprintf("https://api.clarifai.com/v1/token/") req, err := http.NewRequest("POST", url, formData) if err != nil { return "", err } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") httpClient := &http.Client{} resp, err := httpClient.Do(req) if err != nil { return "", err } defer resp.Body.Close() var record TokenResp if err := json.NewDecoder(resp.Body).Decode(&record); err != nil { return "", err } return record.AccessToken, nil } func getNSFW(token string) ([]float64, error) { // Analyze the image at https://samples.clarifai.com/nsfw.jpg url := fmt.Sprintf("https://api.clarifai.com/v1/tag/?model=nsfw-v1.0&url=https://samples.clarifai.com/nsfw.jpg") req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, err } req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+token) httpClient := &http.Client{} resp, err := httpClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() var record NSFWResp if err := json.NewDecoder(resp.Body).Decode(&record); err != nil { return nil, err } return record.Results[0].Result.Tag.Probs, nil }
The main thing to observe in the above program is the url:
url := fmt.Sprintf(“https://api.clarifai.com/v1/tag/?model=nsfw-v1.0&url=https://samples.clarifai.com/nsfw.jpg")
in the function:
func getNSFW(token string) ([]float64, error) {
and the new structure:
type NSFWResp struct { StatusCode string `json:”status_code”` StatusMsg string `json:”status_msg”` Meta struct { Tag struct { Timestamp float64 `json:”timestamp”` Model string `json:”model”` Config string `json:”config”` } `json:”tag”` } `json:”meta”` Results []struct { Docid float64 `json:”docid”` URL string `json:”url”` StatusCode string `json:”status_code”` StatusMsg string `json:”status_msg”` LocalID string `json:”local_id”` Result struct { Tag struct { Classes []string `json:”classes”` Probs []float64 `json:”probs”` } `json:”tag”` } `json:”result”` DocidStr string `json:”docid_str”` } `json:”results”` }
When you run the program:
go run mynsfw.go
The output you get is:
Safe for work, you can trust Bob again (for now)!
That’s it!