Torch、Java、Milvus快速搭建以图搜图系统
Torch、Java、Milvus快速搭建以图搜图系统
1 原理概述
以图搜图⼤致原理(⼝⽔话版)
以图搜图,即通过⼀张图⽚去匹配数据库中的图⽚,到最相似的N张图。在我们普通的搜索系统中,⽂字匹配的搜索单纯的MySQL数据库就能实现简单的搜索,但是图⽚就存在很多难点。
1、⾸先要解决的是图⽚怎么表达的问题,肯定不会是每个像素点去匹配,⽽是对图像提取特征。在传统的数字图像处理中,图像的特征有很多:颜⾊特征、纹理特征、关键点特征、⼏何特征,可以将具有代表性的特征提取处理归⼀化后形成⼀个多维向量去表⽰图⽚。在深度学习如⽕如荼的时代,卷积神经⽹络能更好的做到特征提取这个⼯作。
2、特征提取到了,⾃然⽽然的就是将每个图⽚的特征(即⼀个向量)存⼊数据库,要搜索⼀张图⽚时就去数据库匹配。第⼆个问题就是如何去匹配图⽚,两个向量相等?当然不是。我们⽤距离来表达两个向量的相似程度,距离越近就越相似。距离⽤得最多的就是欧式距离和余弦距离(简单来说区别就是欧⽒距离体现数值上的差异、余弦距离体现⽅向上的相对差异)。
3、怎么判断两个图⽚是否相似解决了,通过距离!第三个问题:来⼀张图时去数据库查询怎么查?⼀个⼀个匹配,最后排个序?当然不是!MySQL可以建索引,这个好像建索引也⽆从下⼿。这⾥就需要借助向量搜索引擎了。⽬前开源的向量搜索引擎还是有很多的,这⾥采⽤Milvus这个开源项⽬实现向量搜索引擎,详细了解的去⾃⾏百度。
2、ResNet提取深度特征向量
环境:Pytorch1.1 python3.6 cuda9.0 采⽤pretrainedmodels库快速搭建ResNet(pip安装即可)
⼏⾏代码搭建出⼀个特征提取⽹络
from torch.autograd import Variable
import torch
as nn
ansforms as transforms
import pretrainedmodels
from PIL import Image
TARGET_IMG_SIZE =224
img_to_tensor = transforms.ToTensor()
def get_seresnet50():
encoder = pretrainedmodels.se_resnet50()
model = nn.Sequential(encoder.layer0,
encoder.layer1,
encoder.layer2,
encoder.layer3,
encoder.layer4,
encoder.avg_pool  # 平均池化,张成⼀个[batchSize,2048]的特征向量)
for param in model.parameters():
model.cuda()# 使⽤GPU,CPU版去掉
model.eval()
return model
# 特征提取
def extract_feature(model, imgpath):
img = Image.open(imgpath)# 读取图⽚
img = size((TARGET_IMG_SIZE, TARGET_IMG_SIZE))
tensor = img_to_tensor(img)# 将图⽚矩阵转化成tensor
tensor = tensor.cuda()# GPU
tensor = torch.unsqueeze(tensor,0)
result = model(Variable(tensor))
result_npy = result.data.cpu().numpy()[0].ravel().tolist()
return result_npy
利⽤serverSocket搭建服务器端,Java端通信,调⽤python的特征提取。
import socket
import threading
import json
from model import extract_feature, get_seresnet50
def main():
# 创建服务器套接字
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 获取本地主机名称
host = hostname()
# 设置⼀个端⼝
port =12345
# 将套接字与本地主机和端⼝绑定
serversocket.bind((host, port))
# 设置监听最⼤连接数
serversocket.listen(10)
# 模型创建
model = get_seresnet50()
print("等待连接")
while True:
# 获取⼀个客户端连接
clientsocket, addr = serversocket.accept()
print("连接地址:%s"%str(addr))
try:
t = ServerThreading(model, clientsocket)# 为每⼀个请求开启⼀个处理线程
t.start()
except Exception as identifier:
print(identifier)
pass
serversocket.close()
pass
class ServerThreading(threading.Thread):
def__init__(self, model, clientsocket, recvsize=1024*1024, encoding="utf-8"):        threading.Thread.__init__(self)
self._socket = clientsocket
self._recvsize = recvsize
self._encoding = encoding
pass
def run(self):
print("开启线程.....")
try:
# 接受数据
msg =''
while True:
# 读取recvsize个字节
rec = self._v(self._recvsize)
# 解码
msg += rec.decode(self._encoding)
# ⽂本接受是否完毕,因为python socket不能⾃⼰判断接收数据是否完毕,
# 所以需要⾃定义协议标志数据接受完毕
if msg.strip().endswith('over'):
msg = msg[:-4]
break
# 解析json格式的数据
# 调⽤神经⽹络模型处理请求
res = extract_del, msg)
sendmsg = json.dumps(res)
print(sendmsg)
# 发送数据
self._socket.send(("%s"% sendmsg).encode(self._encoding))
except Exception as identifier:
self._socket.send("500".encode(self._encoding))
print(identifier)
pass
finally:
self._socket.close()
print("任务结束.....")
if __name__ =="__main__":
main()
3、Java端调⽤Python函数提取特征
package top.maolaoe.imgsearch.service;
import org.springframework.stereotype.Service;
import java.io.*;
import java.Socket;
import java.util.ArrayList;
import java.util.List;
/**
* 通过socket调⽤python获得图⽚的特征向量
*/
@Service
public class FeatureService {
private String HOST ="192.168.1.103";
private final int PORT =12345;
public List<Float>remoteCall(String path){
/
/ 访问服务进程的套接字
//        System.out.println("调⽤远程接⼝:host=>"+HOST+",port=>"+PORT);
try(Socket socket =new Socket(HOST, PORT)){
// 初始化套接字,设置访问服务的主机和进程端⼝号,HOST是访问python进程的主机名称,可以是IP地址或者域名,PORT是python进程绑定的端⼝号// 获取输出流对象
OutputStream os = OutputStream();
PrintStream out =new PrintStream(os);
// 发送内容
out.print(path);
// 告诉服务进程,内容发送完毕,可以开始处理
out.print("over");
// 获取服务进程的输⼊流
InputStream is = InputStream();
BufferedReader br =new BufferedReader(new InputStreamReader(is,"utf-8"));
String tmp = null;
StringBuilder sb =new StringBuilder();
// 读取内容
while((adLine())!=null)
sb.append(tmp).append('\n');
// 解析结果
tmp = sb.toString().substring(1, sb.length()-2);
String[] split = tmp.split(",");
List<Float> list =new ArrayList<Float>(split.length);
for(int i =0; i < split.length; i++){
list.add(Float.valueOf(split[i]));
split[i]= null;
}
//            System.out.println(list);
//            System.out.println(list.size());
return list;
}catch(IOException e){
e.printStackTrace();
}
return null;
}
public static void main(String[] args)throws IOException {
FeatureService featureService =new FeatureService();
}
}
4、安装启动milvus向量搜索引擎
注意修改配置⽂件中的内存⼤⼩以适应⾃⼰的机器,否则docker启动时报错。
5、编写milvus插⼊和搜索向量的⽅法
引⼊依赖:milvus中的guava容易与其他包冲突,单独引⼊
<dependency>
<groupId>io.milvus</groupId>
<artifactId>milvus-sdk-java</artifactId>
<exclusions>
<exclusion>
<groupId&le.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
<version>0.8.2</version>
</dependency>
提供search和insert功能
package top.maolaoe.imgsearch.service;
le.gson.JsonObject;
import io.milvus.client.*;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class MilvusService {
private MilvusClient client =new MilvusGrpcClient();
private String collectionName;
MilvusService(String host,int port, String collectionName,int nlist){
/
/建⽴连接
ConnectParam connectParam =new ConnectParam.Builder().withHost(host).withPort(port).build();
try{
//连接正常则创建collection
Response responseCollect =createCollect(collectionName,2048,1024, MetricType.IP);
System.out.Message());
//创建索引
if(createIndex(nlist)){
System.out.println("创建索引");
}else{
System.out.println("索引创建失败");
}
}catch(ConnectFailedException e){
System.out.println("连接失败!");
}
}
MilvusService(String host,int port, String collectionName){
this(host, port, collectionName,1000);
python转java代码}
MilvusService(){
this("localhost",19530,"imgsearch");
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。