Python中SQLite/SQLAlchemy数值转换及人脸编码恢复咨询
我来帮你一步步解决这两个问题:
一、在Python的SQLite或SQLAlchemy中进行数值转换
SQLite本身是弱类型数据库,但我们可以在Python层面或者通过SQLAlchemy的类型约束来处理数值转换,分两种场景来说:
1. 原生SQLite操作
直接用sqlite3库时,你可以在插入/查询时手动用Python的类型转换函数(比如int()、float())处理数据:
import sqlite3 # 连接数据库 conn = sqlite3.connect('test.db') c = conn.cursor() # 创建表 c.execute("CREATE TABLE IF NOT EXISTS numbers (id INTEGER PRIMARY KEY AUTOINCREMENT, value TEXT)") # 插入时:把数值转成字符串存入 c.execute("INSERT INTO numbers (value) VALUES (?)", (str(12.34),)) # 查询时:把取出的字符串转成数值 c.execute("SELECT value FROM numbers WHERE id=1") result = c.fetchone() if result: num = float(result[0]) print(type(num)) # <class 'float'> conn.commit() conn.close()
如果希望数据库字段更贴合数值类型,也可以直接存储数值,SQLite会自动适配,查询时直接得到对应类型:
# 插入时直接传数值 c.execute("INSERT INTO numbers (value) VALUES (?)", (12.34,)) # 查询时直接得到float类型 result = c.fetchone() print(type(result[0])) # <class 'float'>
2. SQLAlchemy操作
SQLAlchemy提供了强类型的列定义,会自动帮你处理Python和数据库之间的类型映射,常用的数值类型有Integer、Float、Numeric等:
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, Float engine = create_engine('sqlite:///test.db', echo=True) meta = MetaData() # 定义带数值类型的表 numbers = Table( 'numbers', meta, Column('id', Integer, primary_key=True), Column('value', Float) # 指定为Float类型 ) meta.create_all(engine) # 插入时直接传Python数值,SQLAlchemy自动处理转换 with engine.connect() as conn: conn.execute(numbers.insert().values(value=12.34)) # 查询时直接得到float类型的结果 with engine.connect() as conn: result = conn.execute(numbers.select().where(numbers.c.id == 1)).fetchone() if result: print(type(result.value)) # <class 'float'>
如果需要自定义数值转换逻辑(比如处理特殊格式的数值),还可以用SQLAlchemy的TypeDecorator来实现自定义类型转换。
二、恢复存入SQLite的人脸编码数组
首先要说明:你遇到的二进制问题,是因为直接把numpy数组(face_recognition返回的编码是numpy数组)当成普通数据存入了数据库,SQLite自动把这个Python对象序列化成了二进制。正确的做法是先把numpy数组序列化成可存储的格式,再存入数据库,读取时反序列化即可。
正确的存储与恢复方案(推荐用numpy原生序列化)
face_recognition.face_encodings()返回的是一个包含numpy数组的列表,我们可以用numpy的tobytes()把数组转成字节流,存入SQLite的BLOB类型字段,读取时用numpy.frombuffer()恢复成原数组。
修正后的存储代码
import sqlite3 import numpy as np import face_recognition # 加载图片并获取人脸编码 new_picture = face_recognition.load_image_file("你的图片路径.jpg") face_encodings = face_recognition.face_encodings(new_picture) if face_encodings: # 确保检测到人脸 encoding = face_encodings[0] # 取第一个人脸的编码 conn = sqlite3.connect('image.db') c = conn.cursor() # 创建带BLOB字段的表(专门存字节流) c.execute("CREATE TABLE IF NOT EXISTS img(id INTEGER PRIMARY KEY AUTOINCREMENT, imagecode BLOB)") # 把numpy数组转成字节存入 c.execute("INSERT INTO img(imagecode) VALUES (?)", (encoding.tobytes(),)) conn.commit() c.close() conn.close()
恢复原始人脸编码的代码
import sqlite3 import numpy as np conn = sqlite3.connect('image.db') c = conn.cursor() # 查询指定记录的人脸编码 c.execute("SELECT imagecode FROM img WHERE id=1") result = c.fetchone() if result: # 从二进制字节恢复成numpy数组,注意人脸编码是float64类型 original_encoding = np.frombuffer(result[0], dtype=np.float64) print(original_encoding) # 这就是你需要的原始人脸编码数组 c.close() conn.close()
用SQLAlchemy实现的版本
如果你习惯用SQLAlchemy,可以用LargeBinary类型来存储字节流,逻辑和原生SQLite一致:
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, LargeBinary import numpy as np import face_recognition new_picture = face_recognition.load_image_file("你的图片路径.jpg") face_encodings = face_recognition.face_encodings(new_picture) if face_encodings: encoding = face_encodings[0] engine = create_engine('sqlite:///image.db', echo=True) meta = MetaData() img = Table( 'img', meta, Column('id', Integer, primary_key=True), Column('imagecode', LargeBinary), # 用LargeBinary对应BLOB类型 ) meta.create_all(engine) # 存储 with engine.connect() as conn: conn.execute(img.insert().values(imagecode=encoding.tobytes())) # 恢复 with engine.connect() as conn: result = conn.execute(img.select().where(img.c.id == 1)).fetchone() if result: original_encoding = np.frombuffer(result.imagecode, dtype=np.float64) print(original_encoding)
你的原有代码问题说明
看你提供的两段代码,还有几个小错误需要注意:
- 第一段SQLAlchemy代码:创建的表是
img,但插入时用的是students表,表名不匹配;而且把imagecode设为String类型,直接传入numpy数组列表,导致存储的是对象的字符串表示,不是有效数据。 - 第二段原生SQLite代码:创建的表是
img,插入时用的是image表,表名不匹配;而且传入参数时用了(x),这是列表本身,应该用(x[0],)(取第一个编码数组),否则会触发参数数量不匹配的错误。
内容的提问来源于stack exchange,提问作者Aref Othman

