Skip to content

Commit

Permalink
Merge pull request #1157 from shiyu22/image-search-src
Browse files Browse the repository at this point in the history
Update image search with latest Milvus and Towhee
  • Loading branch information
JackLCL authored Jun 26, 2023
2 parents f24dc78 + 71f1b23 commit 2bb6c85
Show file tree
Hide file tree
Showing 17 changed files with 102 additions and 108 deletions.
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ And more solutions you can refer to the [**Examples**](https://github.com/towhee
</td>
<td>
<p>- <a href="https://github.com/towhee-io/examples/tree/main/image/reverse_image_search">Jupyter notebook</a></p>
<p>- <a href="solutions/image/reverse_image_search/quick_deploy">Quick deploy</a></p>
<p>- <a href="solutions/image/reverse_image_search">Quick deploy</a></p>
</td>
<td>
<p>- <a href="https://zhuanlan.zhihu.com/p/517360724">10 行代码搞定以图搜图</a></p>
Expand Down Expand Up @@ -125,7 +125,7 @@ And more solutions you can refer to the [**Examples**](https://github.com/towhee
</td>
<td>
<p>-<a href="https://github.com/towhee-io/examples/tree/main/nlp/question_answering"> Jupyter notebook</a></p>
<p>-<a href="solutions/nlp/question_answering_system/quick_deploy"> Quick deploy</a></p>
<p>-<a href="solutions/nlp/question_answering_system"> Quick deploy</a></p>
</td>
<td>
<p>-<a href="https://mp.weixin.qq.com/s/BZp4CMv2yuVb0oEyuDKNkw">快速搭建对话机器人,就用这一招!</a></p>
Expand Down Expand Up @@ -238,7 +238,6 @@ And more solutions you can refer to the [**Examples**](https://github.com/towhee
<td>- <a href="https://www.bilibili.com/video/BV1dD4y1D7zS">中文</a></td>
</tr>
</table>

### :clapper: Live Demo

We have built [online demos](https://milvus.io/milvus-demos/) for reverse image search, chatbot and molecular search that everyone can have fun with.
Expand Down
79 changes: 42 additions & 37 deletions solutions/image/reverse_image_search/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This demo uses [towhee](https://github.com/towhee-io/towhee) image embedding ope

The system architecture is as below:

<img src="pic/demo.jpg" width = "450" height = "600" alt="system_arch" align=center />
<img src="pic/workflow.png" height = "500" alt="arch" align=center />


## Data Source
Expand All @@ -21,7 +21,8 @@ Download location: https://drive.google.com/file/d/1n_370-5Stk4t0uDV1QqvYkcvyV8r

### Requirements

- [Milvus 2.0](https://milvus.io/docs/v2.0.0/install_standalone-docker.md)
- [Milvus 2.2.10](https://milvus.io/)
- [Towhee](https://towhee.io/)
- [MySQL](https://hub.docker.com/r/mysql/mysql-server)
- [Python3](https://www.python.org/downloads/)
- [Docker](https://docs.docker.com/engine/install/)
Expand All @@ -33,11 +34,10 @@ The reverse image search system requires Milvus, MySQL, WebServer and WebClient

- Modify docker-compose.yaml to map your data directory to the docker container of WebServer
```bash
$ git clone https://github.com/milvus-io/bootcamp.git
$ cd solutions/image/reverse_image_search/quick_deploy
$ wget https://raw.githubusercontent.com/milvus-io/bootcamp/master/solutions/image/reverse_image_search/docker-compose.yaml
$ vim docker-compose.yaml
```
> Change line 73: `./data:/data` --> `your_data_path:/data`
**Then to change line 75:** `./data:/data` --> `your_data_path:/data`

- Create containers & start servers with docker-compose.yaml
```bash
Expand All @@ -59,28 +59,33 @@ Creating img-search-webserver ... done
And show all containers with `docker ps`, and you can use `docker logs img-search-webserver` to get the logs of **server** container.

```bash
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
25b4c8e13590 milvusbootcamp/img-search-server:towhee "/bin/sh -c 'python3…" 59 seconds ago Up 49 seconds 0.0.0.0:5000->5000/tcp img-search-webserver
ae9a9a783952 milvusdb/milvus:v2.0.0-rc8-20211104-d1f4106 "/tini -- milvus run…" 59 seconds ago Up 58 seconds 0.0.0.0:19530->19530/tcp milvus-standalone
7e88bdf66d96 minio/minio:RELEASE.2020-12-03T00-03-10Z "/usr/bin/docker-ent…" About a minute ago Up 59 seconds (healthy) 9000/tcp milvus-minio
4a3ea5fff0f9 mysql:5.7 "docker-entrypoint.s…" About a minute ago Up 59 seconds 0.0.0.0:3306->3306/tcp, 33060/tcp img-search-mysql
f3c7440d5dc4 milvusbootcamp/img-search-client:1.0 "/bin/bash -c '/usr/" About a minute ago Up 59 seconds (health: starting) 0.0.0.0:8001->80/tcp img-search-webclient
cc6b473d905d quay.io/coreos/etcd:v3.5.0 "etcd -advertise-cli…" About a minute ago Up 59 seconds 2379-2380/tcp milvus-etcd
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f24ddb15a529 milvusbootcamp/img-search-server:2.2.10 "/bin/sh -c 'python3…" 35 minutes ago Up 34 minutes 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp img-search-webserver
d26b5cf21822 milvusdb/milvus:v2.2.10 "/tini -- milvus run…" 35 minutes ago Up 35 minutes 0.0.0.0:9091->9091/tcp, :::9091->9091/tcp, 0.0.0.0:19530->19530/tcp, :::19530->19530/tcp milvus-standalone
1e9254f56006 minio/minio:RELEASE.2023-03-20T20-16-18Z "/usr/bin/docker-ent…" 35 minutes ago Up 35 minutes (healthy) 9000/tcp milvus-minio
445cef3fe474 milvusbootcamp/img-search-client:2.2.10 "/docker-entrypoint.…" 35 minutes ago Up 35 minutes (unhealthy) 0.0.0.0:8001->80/tcp, :::8001->80/tcp img-search-webclient
d9bbab39f325 mysql:5.7 "docker-entrypoint.s" 35 minutes ago Up 35 minutes 33060/tcp, 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp img-search-mysql
807c2ace0b28 quay.io/coreos/etcd:v3.5.5 "etcd -advertise-cli…" 35 minutes ago Up 35 minutes 2379-2380/tcp milvus-etcd
```

## Option 2: Deploy with source code

We recommend using Docker Compose to deploy the reverse image search system. However, you also can run from source code, you need to manually start [Milvus](https://milvus.io/docs/v2.0.0/install_standalone-docker.md) and [Mysql](https://dev.mysql.com/doc/mysql-installation-excerpt/5.7/en/docker-mysql-getting-started.html). Next show you how to run the API server and Client.
We recommend using Docker Compose to deploy the reverse image search system. However, you also can run from source code, you need to manually start [Milvus](https://milvus.io) and [Mysql](https://dev.mysql.com/doc/mysql-installation-excerpt/5.7/en/docker-mysql-getting-started.html). Next show you how to run the API server and Client.

### 1. Start Milvus & Mysql

First, you need to start Milvus & Mysql servers.

Refer [Milvus Standalone](https://milvus.io/docs/v2.0.0/install_standalone-docker.md) for how to install Milvus. Please note the Milvus version should match pymilvus version in [config.py](./server/src/config.py).
Refer [Milvus Standalone](https://milvus.io/docs/install_standalone-docker.md) for how to install Milvus.

```bash
$ wget https://github.com/milvus-io/milvus/releases/download/v2.2.10/milvus-standalone-docker-compose.yml -O docker-compose.yml
$ sudo docker-compose up -d
```

There are several ways to start Mysql. One option is using docker to create a container:
```bash
$ docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d --name qa_mysql mysql:5.7
$ docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d --name image_search_mysql mysql:5.7
```


Expand All @@ -90,9 +95,11 @@ Then to start the system server, and it provides HTTP backend services.

- **Install the Python packages**

> Please note the Milvus version should [match pymilvus](https://milvus.io/docs/release_notes.md#Release-Notes) version in [requirements.txt](./server/requirements.txt). And this tutorial uses [milvus 2.2.10](https://milvus.io/docs/v2.2.x/install_standalone-docker.md) and [pymilvus 2.2.11](https://milvus.io/docs/release_notes.md#2210).
```bash
$ git clone https://github.com/milvus-io/bootcamp.git
$ cd solutions/reverse_image_search/quick_deploy/server
$ cd bootcamp/solutions/image/reverse_image_search/server
$ pip install -r requirements.txt
```

Expand Down Expand Up @@ -165,7 +172,7 @@ First, build a container by pulling docker image.
$ docker run -d \
-p 8001:80 \
-e "API_URL=http://${API_HOST}:${API_PORT}" \
milvusbootcamp/img-search-client:1.0
milvusbootcamp/img-search-client:2.2.10
```

## How to use front-end
Expand Down Expand Up @@ -199,24 +206,22 @@ Select an image to search.
If you are interested in our code or would like to contribute code, feel free to learn more about our code structure.

```bash
server
├── Dockerfile
├── requirements.txt
└── src
├── __init__.py
├── config.py # Configuration file
├── encode.py # Convert an image to embedding using towhee pipeline (ResNet50)
├── encode_tf_resnet50.py # Old encoder file using ResNet50 by tensorflow
├── logs.py
├── main.py # Source code to start webserver
├── milvus_helpers.py # Connect to Milvus server and insert/drop/query vectors in Milvus.
├── mysql_helpers.py # Connect to MySQL server, and add/delete/query IDs and object information.
├── operations
│   ├── __init__.py
│   ├── count.py
│   ├── drop.py
│   ├── load.py
│   ├── search.py
│   └── upload.py
└── test_main.py # Pytest file for main.py
Dockerfile
requirements.txt
src
├── __init__.py
├── config.py # Configuration file
├── encode.py # Convert an image to embedding using towhee pipeline (ResNet50)
├── logs.py
├── main.py # Source code to start webserver
├── milvus_helpers.py # Connect to Milvus server and insert/drop/query vectors in Milvus.
├── mysql_helpers.py # Connect to MySQL server, and add/delete/query IDs and object information.
├── operations
│   ├── __init__.py
│   ├── count.py
│   ├── drop.py
│   ├── load.py
│   ├── search.py
│   └── upload.py
└── test_main.py # Pytest file for main.py
```
12 changes: 7 additions & 5 deletions solutions/image/reverse_image_search/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,21 @@ version: '3.5'
services:
etcd:
container_name: milvus-etcd
image: quay.io/coreos/etcd:v3.5.0
image: quay.io/coreos/etcd:v3.5.5
networks:
app_net:
environment:
- ETCD_AUTO_COMPACTION_MODE=revision
- ETCD_AUTO_COMPACTION_RETENTION=1000
- ETCD_QUOTA_BACKEND_BYTES=4294967296
- ETCD_SNAPSHOT_COUNT=50000
volumes:
- ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/etcd:/etcd
command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd

minio:
container_name: milvus-minio
image: minio/minio:RELEASE.2020-12-03T00-03-10Z
image: minio/minio:RELEASE.2023-03-20T20-16-18Z
networks:
app_net:
environment:
Expand All @@ -33,7 +34,7 @@ services:

standalone:
container_name: milvus-standalone
image: milvusdb/milvus:v2.0.2
image: milvusdb/milvus:v2.2.10
networks:
app_net:
ipv4_address: 172.16.238.10
Expand All @@ -45,6 +46,7 @@ services:
- ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/milvus:/var/lib/milvus
ports:
- "19530:19530"
- "9091:9091"
depends_on:
- "etcd"
- "minio"
Expand All @@ -62,7 +64,7 @@ services:

webserver:
container_name: img-search-webserver
image: milvusbootcamp/img-search-server:towhee0.6
image: milvusbootcamp/img-search-server:2.2.10
networks:
app_net:
ipv4_address: 172.16.238.12
Expand All @@ -80,7 +82,7 @@ services:

webclient:
container_name: img-search-webclient
image: milvusbootcamp/img-search-client:1.0
image: milvusbootcamp/img-search-client:2.2.10
networks:
app_net:
ipv4_address: 172.16.238.13
Expand Down
Binary file removed solutions/image/reverse_image_search/pic/demo.jpg
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions solutions/image/reverse_image_search/server/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ WORKDIR /app/src
COPY . /app

RUN pip3 install -r /app/requirements.txt
RUN python3 encode.py

CMD python3 main.py
5 changes: 3 additions & 2 deletions solutions/image/reverse_image_search/server/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ diskcache==5.2.1
uvicorn==0.13.4
numpy==1.21.3
pydantic==1.8.2
pymilvus==2.0.2
towhee==1.0.0rc1
pymilvus==2.2.11
towhee==1.1.0
towhee.models==1.1.0
fastapi==0.65.2
python-multipart==0.0.5
pillow==8.4.0
Expand Down
6 changes: 6 additions & 0 deletions solutions/image/reverse_image_search/server/src/encode.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,9 @@ def __init__(self):
def resnet50_extract_feat(self, img_path):
feat = self.image_embedding_pipe(img_path)
return feat.get()[0]


if __name__ == '__main__':
MODEL = Resnet50()
# Warm up the model to build image
MODEL.resnet50_extract_feat('https://raw.githubusercontent.com/towhee-io/towhee/main/towhee_logo.png')
6 changes: 0 additions & 6 deletions solutions/image/reverse_image_search/server/src/logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,3 @@ def write_log():


LOGGER = write_log()
# if __name__ == "__main__":
# message = 'test writing logs'
# logger = write_log()
# logger.info(message)
# logger.debug(message)
# logger.error(message)
2 changes: 0 additions & 2 deletions solutions/image/reverse_image_search/server/src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ async def upload_images(image: UploadFile = File(None), url: str = None, table_n
# Save the upload image to server.
if image is not None:
content = await image.read()
print('read pic succ')
img_path = os.path.join(UPLOAD_PATH, image.filename)
with open(img_path, "wb+") as f:
f.write(content)
Expand All @@ -103,7 +102,6 @@ async def search_images(image: UploadFile = File(...), topk: int = Form(TOP_K),
try:
# Save the upload image to server.
content = await image.read()
print('read pic succ')
img_path = os.path.join(UPLOAD_PATH, image.filename)
with open(img_path, "wb+") as f:
f.write(content)
Expand Down
44 changes: 16 additions & 28 deletions solutions/image/reverse_image_search/server/src/milvus_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@

class MilvusHelper:
"""
Say something about the ExampleCalass...
Args:
args_0 (`type`):
...
Milvus Helper
"""
def __init__(self):
try:
Expand All @@ -23,60 +19,52 @@ def __init__(self):

def set_collection(self, collection_name):
try:
if self.has_collection(collection_name):
self.collection = Collection(name=collection_name)
else:
raise Exception(f"There is no collection named:{collection_name}")
self.collection = Collection(name=collection_name)
except Exception as e:
LOGGER.error(f"Failed to load data to Milvus: {e}")
LOGGER.error(f"Failed to set collection in Milvus: {e}")
sys.exit(1)

def has_collection(self, collection_name):
# Return if Milvus has the collection
try:
return utility.has_collection(collection_name)
except Exception as e:
LOGGER.error(f"Failed to load data to Milvus: {e}")
LOGGER.error(f"Failed to get collection info to Milvus: {e}")
sys.exit(1)

def create_collection(self, collection_name):
# Create milvus collection if not exists
try:
if not self.has_collection(collection_name):
field1 = FieldSchema(name="id", dtype=DataType.INT64, descrition="int64", is_primary=True, auto_id=True)
field2 = FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, descrition="float vector",
dim=VECTOR_DIMENSION, is_primary=False)
schema = CollectionSchema(fields=[field1, field2], description="collection description")
self.collection = Collection(name=collection_name, schema=schema)
LOGGER.debug(f"Create Milvus collection: {collection_name}")
else:
self.set_collection(collection_name)
field1 = FieldSchema(name="id", dtype=DataType.INT64, descrition="int64", is_primary=True, auto_id=True)
field2 = FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, descrition="float vector",
dim=VECTOR_DIMENSION, is_primary=False)
schema = CollectionSchema(fields=[field1, field2], description="collection description")
self.collection = Collection(name=collection_name, schema=schema)
LOGGER.debug(f"Create Milvus collection: {collection_name}")
return "OK"
except Exception as e:
LOGGER.error(f"Failed to load data to Milvus: {e}")
LOGGER.error(f"Failed create collection in Milvus: {e}")
sys.exit(1)

def insert(self, collection_name, vectors):
# Batch insert vectors to milvus collection
try:
self.create_collection(collection_name)
data = [vectors]
self.set_collection(collection_name)
data = [vectors]
mr = self.collection.insert(data)
ids = mr.primary_keys
self.collection.load()
LOGGER.debug(
f"Insert vectors to Milvus in collection: {collection_name} with {len(vectors)} rows")
return ids
except Exception as e:
LOGGER.error(f"Failed to load data to Milvus: {e}")
LOGGER.error(f"Failed to insert data to Milvus: {e}")
sys.exit(1)

def create_index(self, collection_name):
# Create IVF_FLAT index on milvus collection
try:
self.set_collection(collection_name)
default_index = {"index_type": "IVF_SQ8", "metric_type": METRIC_TYPE, "params": {"nlist": 16384}}
default_index = {"metric_type": METRIC_TYPE, "index_type": "IVF_FLAT", "params": {"nlist": 2048}}
status = self.collection.create_index(field_name="embedding", index_params=default_index)
if not status.code:
LOGGER.debug(
Expand All @@ -103,10 +91,9 @@ def search_vectors(self, collection_name, vectors, top_k):
# Search vector in milvus collection
try:
self.set_collection(collection_name)
self.collection.load()
search_params = {"metric_type": METRIC_TYPE, "params": {"nprobe": 16}}
# data = [vectors]
res = self.collection.search(vectors, anns_field="embedding", param=search_params, limit=top_k)
print(res[0])
LOGGER.debug(f"Successfully search in collection: {res}")
return res
except Exception as e:
Expand All @@ -117,6 +104,7 @@ def count(self, collection_name):
# Get the number of milvus collection
try:
self.set_collection(collection_name)
self.collection.flush()
num = self.collection.num_entities
LOGGER.debug(f"Successfully get the num:{num} of the collection:{collection_name}")
return num
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +0,0 @@
import logging
logging.basicConfig(filename='app.log', filemode='w', format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
Loading

0 comments on commit 2bb6c85

Please sign in to comment.