Quickstart: Get image insights using the Bing Visual Search REST API and Go
Use this quickstart to make your first call to the Bing Visual Search API using the Go programming language. A POST request uploads an image to the API endpoint. The results include URLs and descriptive information about images similar to the uploaded image.
Prerequisites
- Install the Go binaries.
- Install the go-spew deep pretty printer, which is used to display results. To install go-spew, use the
$ go get -u https://github.com/davecgh/go-spew
command.
Project and libraries
Create a Go project in your IDE or editor. Then, import net/http
for requests, ioutil
to read the response, and encoding/json
to handle the JSON text of results. Use the go-spew
library to parse JSON results.
package main
import (
"bytes"
"io"
"fmt"
"io/ioutil"
"mime/multipart"
"net/http"
"os"
"path/filepath"
"encoding/json"
"github.com/davecgh/go-spew/spew"
)
Struct to format results
The BingAnswer
structure formats data returned in the JSON response, which is multilevel and complex. The following implementation covers some of the essentials:
type BingAnswer struct {
Type string `json:"_type"`
Instrumentation struct {
Type string `json:"_type"`
PingUrlBase string `json:"_pingUrlBase"`
PageLoadPingUrl string `json:"_pageLoadPingUrl"`
} `json:"instrumentation"`
Tags []struct {
DisplayName string `json:"displayName"`
Actions []struct {
Type string `json:"_type"`
ActionType string `json:"actionType"`
Data struct {
Value []struct {
WebSearchUrl string `json:"webSearchUrl"`
Name string `json:"name"`
}`json:"value"`
ImageInsightsToken string `json:"imageInsightsToken"`
InsightsMetadata struct {
PagesIncludingCount int `json:"pagesIncludingCount"`
AvailableSizesCount int `json:"availableSizesCount"`
}
ImageId string `json:"imageId"`
AccentColor string `json:"accentColor"`
} `json:"data"`
} `json:"actions"`
} `json:"tags"`
Image struct {
WebSearchUrl string `json:"webSearchUrl"`
WebSearchUrlPingSuffix string `json:"webSearchUrlPingSuffix"`
Name string `json:"name"`
IsFamilyFriendly bool `json:"isFamilyFriendly"`
ContentSize string `json:"contentSize"`
EncodingFormat string `json:"encodingFormat"`
HostPageDisplayUrl string `json:"hostPageDisplayUrl"`
Width int `json:"width"`
Height int `json:"height"`
Thumbnail struct {
Width int `json:"width"`
Height int `json:"height"`
}
InsightsMetadata struct {
PagesIncludingCount int `json:"pagesIncludingCount"`
AvailableSizesCount int `json:"availableSizesCount"`
}
AccentColor string `json:"accentColor"`
VisualWords string `json:"visualWords"`
} `json:"image"`
}
Main function and variables
The following code declares the main function and assigns the required variables:
- Confirm that the endpoint is correct and replace the
token
value with a valid subscription key from your Azure account. - For
batchNumber
, assign a GUID, which is required for the leading and trailing boundaries of the POST data. - For
fileName
, assign the image file to use for the POST.
func main() {
// Verify the endpoint URI and replace the token string with a valid subscription key.se
endpoint := "https://api.bing.microsoft.com/bing/v7.0/images/visualsearch"
token := "YOUR-ACCESS-KEY"
client := &http.Client{}
batchNumber := "d7ecc447-912f-413e-961d-a83021f1775f"
fileName := "ElectricBike.JPG"
body, contentType := createRequestBody(fileName, batchNumber)
req, err := http.NewRequest("POST", endpoint, body)
req.Header.Add("Content-Type", contentType)
req.Header.Set("Ocp-Apim-Subscription-Key", token)
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
resbody, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
//fmt.Print(string(resbody))
// Create a new answer.
ans := new(BingAnswer)
err = json.Unmarshal(resbody, &ans)
if err != nil {
fmt.Println(err)
}
fmt.Print("Output of BingAnswer: \r\n\r\n")
// Iterate over search results and print the result name and URL.
for _, result := range ans.Tags {
spew.Dump(result)
}
}
Boundaries of POST body
A POST request to the Visual Search endpoint requires leading and trailing boundaries to enclose the POST data. These functions aren't included in the main()
block.
The leading boundary includes a batch number, the content type identifier Content-Disposition: form-data; name="image"; filename=
, and the filename of the image to POST.
The trailing boundary includes the batch number only.
func BuildFormDataStart(batNum string, fileName string) string{
startBoundary := "--batch_" + batNum + "\r\n"
requestBoundary := startBoundary + "\r\n" + "Content-Disposition: form-data; name=\"image\"; filename=" + fileName + "\r\n\r\n"
return requestBoundary
}
func BuildFormDataEnd(batNum string) string{
return "\r\n" + "\r\n" + "--batch_" + batNum + "\r\n"
}
Add image bytes to POST body
The following code creates the POST request that contains image data:
func createRequestBody(fileName string, batchNumber string) (*bytes.Buffer, string) {
file, err := os.Open(fileName)
if err != nil {
panic(err)
}
defer file.Close()
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
writer.SetBoundary(BuildFormDataStart(batchNumber, fileName))
part, err := writer.CreateFormFile("image", filepath.Base(file.Name()))
if err != nil {
panic(err)
}
io.Copy(part, file)
writer.WriteField("lastpart", BuildFormDataEnd(batchNumber))
writer.Close()
return body, writer.FormDataContentType()
}
Send the request
The following code sends the request and reads the results:
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
resbody, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
Handle the response
The Unmarshall
function extracts information from the JSON text returned by the Visual Search API. The go-spew
pretty printer displays the results.
// Create a new answer.
ans := new(BingAnswer)
err = json.Unmarshal(resbody, &ans)
if err != nil {
fmt.Println(err)
}
fmt.Print("Output of BingAnswer: \r\n\r\n")
// Iterate over search results and print the result name and URL.
for _, result := range ans.Tags {
spew.Dump(result)
}
Note
Francesco Giordano contributed code to this example.
Results
The results identify images similar to the image contained in the POST body. The useful fields are WebSearchUrl
and Name
.
Value: ([]struct { WebSearchUrl string "json:\"webSearchUrl\""; Name string "json:\"name\"" }) (len=66 cap=94) {
(struct { WebSearchUrl string "json:\"webSearchUrl\""; Name string "json:\"name\"" }) {
WebSearchUrl: (string) (len=129) "https://www.bing.com/images/search?view=detailv2&FORM=OIIRPO&id=B9E6621161769D578A9E4DD9FD742128DE65225A&simid=608046863828453626",
Name: (string) (len=87) "26\"Foldable Electric Mountain Bike Bicycle Ebike W/Lithium Battery 250W 36V MY8L | eBay"
},
(struct { WebSearchUrl string "json:\"webSearchUrl\""; Name string "json:\"name\"" }) {
WebSearchUrl: (string) (len=129) "https://www.bing.com/images/search?view=detailv2&FORM=OIIRPO&id=5554757348A9E88E9EEAB74217E228689E716B61&simid=607988237543409883",
Name: (string) (len=96) "Best 25+ Electric mountain bike ideas on Pinterest | Mountain bicycle, Mtb mountain bike and ..."
},
(struct { WebSearchUrl string "json:\"webSearchUrl\""; Name string "json:\"name\"" }) {
WebSearchUrl: (string) (len=129) "https://www.bing.com/images/search?view=detailv2&FORM=OIIRPO&id=B56F626A4803D829FE326842E2B97CE5B87F17F2&simid=607991699288949513",
Name: (string) (len=100) "Electric Fat Bike Mountain Bicycle Snow Bike Cruiser Ebike Motor Lithium Battery Dual Oil Brakes ..."
},
(struct { WebSearchUrl string "json:\"webSearchUrl\""; Name string "json:\"name\"" }) {
WebSearchUrl: (string) (len=129) "https://www.bing.com/images/search?view=detailv2&FORM=OIIRPO&id=CCAE400EE510DCA6F5740ABAF08A5310B4EF0698&simid=608003854048890458",
Name: (string) (len=81) "Best Mountain Bikes Under 1500 (Outstanding Performance) | Mountain Bicycle World"
},
(struct { WebSearchUrl string "json:\"webSearchUrl\""; Name string "json:\"name\"" }) {
WebSearchUrl: (string) (len=129) "https://www.bing.com/images/search?view=detailv2&FORM=OIIRPO&id=1244730E2E3F2D5DFBED9A9CFE1DBE27B09F08BC&simid=608005181199745622",
Name: (string) (len=98) "ANCHEER Folding Electric Mountain Bike with 26\" Super Lightweight Magnesium (36V 250W) - GearScoot"
},
(struct { WebSearchUrl string "json:\"webSearchUrl\""; Name string "json:\"name\"" }) {
WebSearchUrl: (string) (len=129) "https://www.bing.com/images/search?view=detailv2&FORM=OIIRPO&id=38AAD876E57BB0D502FBDD5B1CB29EB7EB8DD9E2&simid=608041654062220328",
Name: (string) (len=97) "29 best CB Bikes Gadgets & Accessories images on Pinterest | Bicycles, Bicycling and Bike gadgets"
},
(struct { WebSearchUrl string "json:\"webSearchUrl\""; Name string "json:\"name\"" }) {
WebSearchUrl: (string) (len=129) "https://www.bing.com/images/search?view=detailv2&FORM=OIIRPO&id=6892C8FD22D0E42911C99AFD8FE1FD37BD82E02C&simid=608052803759703185",
Name: (string) (len=98) "ANCHEER Folding Electric Mountain Bike with 26\" Super Lightweight Magnesium (36V 250W) - GearScoot"
},