家·谱——人脸识别家谱系统

家·谱——人脸识别家谱系统

家·谱——智能家谱录入查询是一款基于人脸识别与图像标签的在线家谱查询录入系统,提供从人脸录入、识别到家谱构建展示的一站式服务。使用django+python+paddlehub+BaiduAPI。

支持使用paddlehub或者face_recognition两种本地库路线。

目前的功能有:

  • 人脸录入、人脸识别
  • 社交关系展示
  • 自动家谱构建与展示
  • 地图标点展示
  • 家族合照分析联想

示例部署DEMO:http://124.221.104.193/

github链接:家·谱——智能人脸家谱系统

有私有化部署需求可联系:

  • 邮箱:boyifan1@126.com
  • 手机:13774383668
  • QQ:2424690968

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

环境

python3.6及以上

windows 配置
pip install django
pip install xlwt
pip install pypinpyin
pip install pyecharts
pip install paddlepaddle
pip install paddlehub
安装 Graphviz
linux 配置
sudo apt-get install graphviz
pip install pandas
pip install face_recognition
pip install django
pip install xlwt	
pip install pypinpyin
pip install pyecharts
pip install paddlepaddle
pip install paddlehub

初始化

首先需要去百度智能开放平台申请人脸的api-key

1. 领取免费测试资源

前往百度智能云-管理中心 (baidu.com)点击基础服务->全部免费领取人脸识别测试资源

2. 创建应用

前往百度智能云-管理中心 (baidu.com)按引导创建应用,之后在应用列表中获取api-keysecret-key

在这里插入图片描述

3. 替换api-key和secret-key

cv/cv/codes/FaceExtractor.py

cv/cv/codes/FaceRecognition.py

cv/cv/views.py

这三个文件的头部有

# client_id 为官网获取的AK, client_secret 为官网获取的SK
api_key = "你的api_key"
secret_key = "你的secret_key"

将他们替换成你之前申请的api-key和secret-key。大功告成!

运行

cd cv
python manage.py runserver 0.0.0.0:80

**repo中的数据库已经创建了一个超级用户:用户名 admin 密码 passwd

功能展示与实现

1. 人员录入

作为系统的入口,通过web服务接口实现数据平台的访问,由管理人员/访客上传需要数字化的资源并经由服务端实时处理进行初步的自动化筛选。该功能一共分为三个步骤——上传相片、录入姓名、补充信息。

  • 上传相片

可以在此处上传需要录入人员的照片,可以是多人合照也可以是单人照,之后系统会找出相片中的可识别人脸并进入下一步。

  • 录入姓名

格式化展示从上一步所上传图片中识别出的人脸,并为每一个人脸提供一个输入框以录入姓名,提交后人脸将会被编码成128维数组并存入服务端。之后可以在人脸列表中查询与添加更多信息。

  • 补充信息

这是完成录入姓名后的一个跳转页面,能够方便的跳转到人员的详情页面以编辑和补全信息并使用地图标点与自动家谱构建等功能。

在这里插入图片描述

2. 人员识别

上传一张相片,可以是单人或者多人合照。随后返回与已存储人人脸的比对结果,并提供详情跳转。

  • 上传相片

    可以在此处上传需要识别人员的照片,可以是多人合照也可以是单人照,之后系统会找出相片中的人脸并与已录入的人脸进行比对。

  • 查看结果

返回上一步识别的结果,提供识别结果详情页的跳转与识别数据的下载(包括识别结果的可视化图层叠加相片和EXCEL表格)。

在这里插入图片描述

3. 人员列表

为所有已经录入的人员提供一个统一查询与编辑的入口,同时提供更多详细资料的上传与展示功能。包括人员一览、人员概述、地图标点、家谱展示、图像列表、编辑信息等功能

  • 人员一览

作为人员列表功能的入口,列表展示录入人员的姓名与大头照

  • 人员详情页

集中提供人员概述、地图标点、家谱展示、图像列表、编辑信息等功能

  • 人员概述

展示人员的大头贴及人物简述

  • 地图标点

提供地图标点的展示与详情功能

  • 家谱展示

根据所填资料自动生成家谱,并提供家族成员详情页面的跳转

  • 图像列表

提供所有已上传人脸的展示,也可以跳转到来源图片查看合照信息。

  • 编辑信息

提供所有相关信息的编辑与展示功能,包括生卒年月,家族信息,经纬度标记,概述等。

在这里插入图片描述

4. 其他支持功能
  • 用户登录注册
  • 后台数据管理
  • 合照重识与分析
  • ……

部分功能代码

1. 人脸分割后处理

源代码位置cv/cv/codes/FaceExtractor.py

使用Paddlehubpyramidbox_lite_mobile模型快速分割相片中的的人脸后分割出每一张人脸大头贴。之后将合照与大头贴分别存储进数据库。提供两种预测方式:paddlehub推理或者baiduAPI接口推理。

from PIL import Image,ImageDraw,ImageFont
import glob
import os
from pathlib import Path
from dbmodel.models import FaceImage, People
from dbmodel.models import Image as image_db
import requests
import paddlehub as hub
import cv2

# client_id 为官网获取的AK, client_secret 为官网获取的SK
api_key = "你的api_key"
secret_key = "你的secret_key"

face_detector = hub.Module(name="pyramidbox_lite_mobile")

def face_locations(path):
    results = face_detector.face_detection(images=[cv2.imread(path)])
    print(results)
    locations = []
    for result in results[0]["data"]:
        locations.append([result["top"], result["right"], result["bottom"], result["left"]])
    return locations


def extractor(img_path):
    BASE_DIR = Path(__file__).resolve().parent.parent.parent
    origin = Image.open(img_path)
    locations = face_locations(img_path)
    # 保存合照人头画框
    pil_image = Image.open(img_path)
    draw = ImageDraw.Draw(pil_image)
    font_size = pil_image.size[0] // 30
    ft = ImageFont.truetype(os.path.join(BASE_DIR, 'cv', 'files', 'arialuni.ttf'), font_size)
    face_list = locations
    for i in range(len(face_list)):
        box = (face_list[i][3], face_list[i][0], face_list[i][1], face_list[i][2])
        draw.rectangle(box, None, 'yellow')
        draw.text((box[0:2]), str(i+1), "red", ft)
    img_path = os.path.join(BASE_DIR, 'cv', 'model_image', os.path.split(img_path)[-1])
    pil_image.save(img_path)
    img_path = os.path.join(BASE_DIR, 'statics', 'temp_image', os.path.split(img_path)[-1])
    image = image_db(path=os.path.split(img_path)[-1], count=len(face_list))
    image.save()
    # 保存各个人头大头贴
    count = 1
    for location in locations:
        box = (location[3], location[0], location[1], location[2])
        face = origin.crop(box)
        save_path = img_path[0:img_path.rfind('.')]+'-'+str(count)+img_path[img_path.rfind("."):]
        face.save(save_path)
        #print(save_path+" saved!")
        count += 1
    return count-1


def baidu_extractor(img_path):
    BASE_DIR = Path(__file__).resolve().parent.parent.parent
    url = 'http://124.221.104.193/static/'+img_path
    print("地址:", url)
    img_path = os.path.join(BASE_DIR, "upload",img_path)

    # 获取access_token
    host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%s&client_secret=%s' % (
    api_key, secret_key)
    response = requests.get(host)
    if response:
        access_token = response.json()["access_token"]
    else:
        return 0

    # 设置请求包体
    request_url = "https://aip.baidubce.com/rest/2.0/face/v3/detect"
    request_url = request_url + "?access_token=" + access_token
    headers = {'content-type': 'application/json'}
    result = []
    params = '{"image":"%s","image_type":"URL","max_face_num":100}' % url
    response = requests.post(request_url, data=params, headers=headers)
    print(response.json())
    if response.json()["error_msg"] != "SUCCESS":
        return 0
    facelist = response.json()["result"]["face_list"]
    locations = []
    origin = Image.open(img_path)
    for face in facelist:
        locations.append(face["location"])
    # 保存合照人头画框
    pil_image = Image.open(img_path)
    draw = ImageDraw.Draw(pil_image)
    font_size = pil_image.size[0] // 30
    ft = ImageFont.truetype(os.path.join(BASE_DIR, 'cv', 'files', 'arialuni.ttf'), font_size)
    face_list = locations
    for i in range(len(face_list)):
        box = (face_list[i]["left"], face_list[i]["top"], face_list[i]["left"] + face_list[i]["width"],face_list[i]["top"] + face_list[i]["height"])
        draw.rectangle(box, None, 'yellow')
        draw.text((box[0:2]), str(i +1), "red", ft)
    img_path = os.path.join(BASE_DIR, 'cv', 'model_image', os.path.split(img_path)[-1])
    pil_image.save(img_path)
    img_path = os.path.join(BASE_DIR, 'statics', 'temp_image', os.path.split(img_path)[-1])
    image = image_db.objects.get(path=os.path.split(img_path)[-1])
    image.count = len(locations)
    image.use_baidu = True
    image.save()
    # 保存各个人头大头贴
    count = 1
    for location in locations:
        box = (location["left"], location["top"], location["left"]+location["width"], location["top"]+location["height"])
        face = origin.crop(box)
        save_path = img_path[0:img_path.rfind('.')] + '-' + str(count) + img_path[img_path.rfind("."):]
        face.save(save_path)
        # print(save_path+" saved!")
        count += 1
    return count - 1

2. 人脸比对及合照后处理

源代码位置cv/cv/codes/FaceRecognition.py

人脸比对功能的后端代码。先分割人脸之后再各个进行1:N的比对。为什么不进行N:M比对是为了让之后的合照重识与补录等增强功能留下可调用的接口。

import base64
import shutil
import time
from io import BytesIO

import numpy as np
import os
import pandas as pd
import requests
from PIL import Image, ImageDraw, ImageFont
from ..settings import BASE_DIR
from django.shortcuts import render
from dbmodel.models import FaceImage, People
from dbmodel.models import Image as image_db
from .FaceExtractor import face_locations
from django.contrib import messages
info_dict = {}
info =""

# client_id 为官网获取的AK, client_secret 为官网获取的SK
api_key = "你的api_key"
secret_key = "你的secret_key"

def initialing():
    return
    # 载入已保存的模型
    global info_dict
    info_dict = {}
    model_saving_path = os.path.join(BASE_DIR, 'cv', 'model')
    face_objs = FaceImage.objects.all()
    for face_obj in face_objs:
        path = face_obj.path
        name = path[:path.rfind('.')]
        path = os.path.join(model_saving_path, face_obj.path)
        info = np.load(path[:path.rfind('.')]+'.npy')
        info_dict[name] = info
    return_str = ""
    for i in info_dict.keys():
        return_str += i + ','
    return return_str

def face_matchng(path,request,tolerance=1):
    img_path = path

    # 获取access_token
    global info
    info = "正在初始化"
    host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%s&client_secret=%s' % (
    api_key, secret_key)
    response = requests.get(host)
    if response:
        access_token = response.json()["access_token"]
    else:
        return 0


    result = []
    info = "正在分割人脸"
    try:    # 使用百度api分割人脸
        try:
            if request.POST["use_baidu"] == "yes":
                pass
            else:
                raise "NotUsingBaidu"
        except:
            image_obj = image_db.objects.get(path=os.path.basename(img_path))
            if not image_obj.use_baidu:
                raise "NotUsingBaidu"
        url = 'http://124.221.104.193/static/upload/' + os.path.basename(img_path)
        print("地址:", url)
        # 设置请求包体
        request_url = "https://aip.baidubce.com/rest/2.0/face/v3/detect"
        request_url = request_url + "?access_token=" + access_token
        headers = {'content-type': 'application/json'}
        result = []
        params = '{"image":"%s","image_type":"URL","max_face_num":100}' % url
        response = requests.post(request_url, data=params, headers=headers)
        print(response.json())
        if response.json()["error_msg"] != "SUCCESS":
            raise "baiduExtractError"
        facelist = response.json()["result"]["face_list"]
        origin_locations = []
        locations = []
        for face in facelist:
            origin_locations.append(face["location"])
        face_list = origin_locations
        for i in range(len(face_list)):
            box = (face_list[i]["left"], face_list[i]["top"], face_list[i]["left"] + face_list[i]["width"],
                   face_list[i]["top"] + face_list[i]["height"])
            locations.append([box[1], box[2], box[3], box[0]])
        print("使用百度api分割人脸")
    except: # 使用face_recognition分割人脸
        locations = face_locations(img_path)
        print("使用本地库分割人脸")
    origin = Image.open(img_path)
    time_now = os.path.basename(img_path)[:os.path.basename(img_path).rfind('.')]
    file_type = os.path.basename(img_path)[os.path.basename(img_path).rfind('.'):]
    # 设置请求包体
    request_url = "https://aip.baidubce.com/rest/2.0/face/v3/multi-search"
    request_url = request_url + "?access_token=" + access_token
    headers = {'content-type': 'application/json'}
    try:
        image_obj = image_db.objects.get(path=os.path.basename(img_path))
        image_obj.count = len(locations)
        image_obj.save()
    except:
        pass
    # 分割人脸并一一调用百度api
    for i in range(len(locations)):
        info = "正在识别第%d个人脸,共%d个" % (i, len(locations))
        result.append([])
        box = (locations[i][3], locations[i][0], locations[i][1], locations[i][2])
        face = origin.crop(box)
        output_buffer = BytesIO()
        pic_save_path = str(time_now) + "-" + str(i+1) + img_path[img_path.rfind("."):]
        pic_save_path = os.path.join(BASE_DIR, "statics", "temp_image", pic_save_path)
        print(pic_save_path)
        face.save(pic_save_path)
        png = open(pic_save_path, 'rb')
        res = png.read()
        png.close()
        image = base64.b64encode(res).decode("ascii")
        params = '{"image":"%s","image_type":"BASE64","group_id_list":"admin","max_user_num":5,"match_threshold":%d}' % (image,int(tolerance*100))
        response = requests.post(request_url, data=params, headers=headers)
        print(response.json())
        if response.json()["error_msg"] == "SUCCESS":
            res_info = response.json()["result"]["face_list"][0]["user_list"]
            for j in range(len(res_info)):
                result[i].append({"id": res_info[j]["user_id"], "score": res_info[j]["score"]})
        else:
            result[i].append(response.json()["error_msg"])
        time.sleep(0.5)

    # 结果格式化与可视化
    info = "正在整合信息"
    recognition_result = []
    df = pd.DataFrame(result)
    df.to_excel(os.path.join(BASE_DIR, 'statics', 'temp_image', time_now+'.xls'))
    pil_image = Image.open(img_path)
    draw = ImageDraw.Draw(pil_image)
    font_size = pil_image.size[0] // 50
    ft = ImageFont.truetype(os.path.join(BASE_DIR, 'cv', 'files', 'arialuni.ttf'), font_size)
    for i in range(len(locations)):
        box = (locations[i][3], locations[i][0], locations[i][1], locations[i][2])
        draw.rectangle(box, None, 'yellow', width=font_size // 8)
        draw.text((box[0:2]), str(i+1), "red", ft)
        recognition_result.append([])
        for j in range(len(result[i])):
            if result[i][j] == "match user is not found":
                name = "未知人脸"
                recognition_result[i].append([name, 0])
            elif type(result[i][j]) == str:
                name = "人脸解析出错"
                recognition_result[i].append([name, 0])
            elif type(result[i][j]) == dict:
                try:
                    name = People.objects.filter(id=result[i][j]["id"])[0].name
                    recognition_result[i].append([name, result[i][j]["score"]])
                    if result[i][j]["score"] == 100:
                        draw.rectangle(box, None, 'lime', width=font_size // 8)
                except:
                    name = "本地库丢失id=%s" % result[i][j]["id"]
                    recognition_result[i].append([name, result[i][j]["score"]])
                    if result[i][j]["score"] == 100:
                        draw.rectangle(box, None, 'lime', width=font_size // 8)
        if recognition_result[i][0][0] == "未知人脸" or recognition_result[i][0][0] == "人脸解析出错":
            continue
        draw.text((box[0], box[1] - font_size), str(recognition_result[i][0][0]), "red", ft)
    img_path = os.path.join('temp_image', time_now + file_type)
    pil_image.save(os.path.join(BASE_DIR, 'statics', img_path))
    return_dic = {'path': img_path, 'result': recognition_result}
    return return_dic


def dict_add(path, name):
    name_path = os.path.split(path)[-1]
    img_path = name_path[:name_path.find("-")]+name_path[name_path.rfind("."):]
    try:
        image_obj = image_db.objects.filter(path=img_path)[0]
    except:
        shutil.copy(os.path.join(BASE_DIR, "upload", img_path), os.path.join(BASE_DIR, "cv", "model_image", img_path))
        image_obj = image_db(path=img_path)
        image_obj.save()
    try:
        pic_save_path = os.path.join(BASE_DIR, 'cv', 'model_image', name+'@'+name_path)
        fpw = open(pic_save_path, "wb")
        fpr = open(path, "rb")
        for line in fpr:
            fpw.write(line)
        fpw.close()
        path = os.path.basename(pic_save_path)
        name = path[:path.find('@')]
        uploadtime = pic_save_path[pic_save_path.find('@') + 1:pic_save_path.rfind('-')]
        uploadtime = time.strftime(r"%Y-%m-%d %H:%M:%S", time.localtime(eval(uploadtime)))
        print(name, uploadtime, path, "\n")
        if People.objects.filter(name=name):
            people = People.objects.filter(name=name)[0]
        else:
            people = People(name=name)
            people.save()
        obj = FaceImage(name=people, path=path, upload_time=uploadtime, image=image_obj)
        obj.save()
        # 百度api上传
        host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%s&client_secret=%s' % (api_key, secret_key)
        response = requests.get(host)
        if response:
            access_token = response.json()["access_token"]
        else:
            return "token获取失败"
        request_url = "https://aip.baidubce.com/rest/2.0/face/v3/faceset/user/add"
        request_url = request_url + "?access_token=" + access_token
        headers = {'content-type': 'application/json'}
        png = open(pic_save_path, 'rb')
        res = png.read()
        png.close()
        image = base64.b64encode(res).decode("ascii")
        params = '{"image":"%s","image_type":"BASE64","group_id":"admin","user_id":"%d"}' % (image, people.id)
        headers = {'content-type': 'application/json'}
        response = requests.post(request_url, data=params, headers=headers)
        if response.json()["error_msg"] !="SUCCESS":
            return "人脸编码失败或图像已存在"
        else:
            obj.token = response.json()["result"]["face_token"]
            obj.logid = response.json()["log_id"]
            obj.save()
    except IndexError:
        print("人脸过于模糊,请提供清晰的正面照")
        return 0
    return 1

3. 家谱绘制

源代码位置cv/cv/views.py

这个比较麻烦,组合了familytreemaker书写dot语言再用Graphviz画图。现在的实现路径不太好,但勉强能用。

def familytree(request, name):
    people_obj = People.objects.get(name=name)
    peo_obj_list = [people_obj]
    path = os.path.join(BASE_DIR, 'statics', 'temp_image', str(time.time()) + '.txt')
    fp = open(path, "w+", encoding="utf-8")
    fp.close()
    check = [people_obj]
    written = set()
    # bfs遍历
    while peo_obj_list:
        peo_now = peo_obj_list[0]
        print(peo_now)
        # 将孩子加入队列
        kids_list = peo_now.kids
        if kids_list:
            for kid in kids_list:
                try:
                    People.objects.get(name=kid)
                    if People.objects.get(name=kid) not in check:
                        peo_obj_list.insert(peo_obj_list.index(peo_now)+1, People.objects.get(name=kid))
                        check.insert(check.index(peo_now)+1, People.objects.get(name=kid))
                except:
                    pass
                finally:
                    pass

        # 将配偶加进队列
        # try:
        #     print(People.objects.get(name=peo_now.mate))
        #     if People.objects.get(name=peo_now.mate) not in check:
        #         peo_obj_list.insert(peo_obj_list.index(peo_now)+1, People.objects.get(name=peo_now.mate))
        #         check.insert(check.index(peo_now)+1, People.objects.get(name=peo_now.mate))
        # except:
        #     pass
        # 将父亲加入队列
        try:
            People.objects.get(name=peo_now.father)
            if People.objects.get(name=peo_now.father) not in check:
                peo_obj_list.insert(peo_obj_list.index(peo_now), People.objects.get(name=peo_now.father))
                check.insert(check.index(peo_now), People.objects.get(name=peo_now.father))
        except:
            pass
        finally:
            pass
        # 将母亲加入队列
        try:
            People.objects.get(name=peo_now.mother)
            if People.objects.get(name=peo_now.mother) not in check:
                peo_obj_list.insert(peo_obj_list.index(peo_now), People.objects.get(name=peo_now.mother))
                check.insert(check.index(peo_now), People.objects.get(name=peo_now.mother))
        except:
            pass
        finally:
            pass



        print(check, "\n")
        print(peo_obj_list, "\n")
        peo_obj_list.remove(peo_now)

    for peo_now in check:
        if peo_now not in written:
            couple_obj = re_familytree(peo_now, path)
            for obj in couple_obj:
                written.add(obj)
    shell = 'python ' + os.path.join(BASE_DIR, 'cv', 'codes', 'familytreemaker.py ') + path
    gra_path = path[:path.rfind('.')]
    fp = open(gra_path, 'w+', encoding="utf-8")
    p = subprocess.run(shell, stdout=subprocess.PIPE, shell=True)
    temp = p.stdout
    temp = temp.decode('cp936')

    fp.write(temp)
    fp.close()
    # os.remove(path)
    shell = "dot -Tpng " + gra_path + " -O"
    subprocess.run(shell, shell=True)
    os.remove(gra_path)
    context = {}
    context["name"] = name
    context["path"] = "temp_image/" + os.path.basename(gra_path) + ".png"
    context["check"] = list(written)
    return context


def re_familytree(people_obj, path):
    # print(people_obj)
    fp = open(path, 'a', encoding="utf-8")
    couple_obj = []
    # 为无对象父母写txt
    flag = 0
    try:
        father = People.objects.get(name=people_obj.father)
    except:
        flag=flag+1
    try:
        father = People.objects.get(name=people_obj.mother)
    except:
        flag = flag+1
    if flag == 2 and people_obj.father and people_obj.mother:
        fp.write(people_obj.mother+"(F,id=%s)\n" % "".join(lazy_pinyin(people_obj.mother)).replace(' ', '').replace(".", ''))
        fp.write(people_obj.father + "(M,id=%s)\n" % "".join(lazy_pinyin(people_obj.father)).replace(' ', '').replace(".", ''))
        fp.write("\t"+people_obj.name+"(id=%d)\n\n" % people_obj.id)
    # 为配偶写txt
    try:
        mate = People.objects.get(name=people_obj.mate)
        fp.write(mate.name + "(id=%d," % mate.id)
        if mate.sex == "female":
            fp.write("F,")
        if mate.sex == "male":
            fp.write("M,")
        if mate.birth_date:
            fp.write("birthday=%s," % str(mate.birth_date)[:-9])
        if mate.death_date:
            fp.write("deathday=%s" % str(mate.death_date)[:-9])
        fp.write(")\n")
        couple_obj.append(mate)
    except:
        mate = people_obj.mate
        if not mate:
            fp.close()
            return [people_obj]
        fp.write(mate +"(id=%s)" % "".join(lazy_pinyin(people_obj.mate)).replace(' ', '').replace(".", '') + "\n")

    # 为自己写txt
    name = people_obj.name
    fp.write(name + "(id=%d," % people_obj.id)
    if people_obj.sex == "female":
        fp.write("F,")
    if people_obj.sex == "male":
        fp.write("M,")
    if people_obj.birth_date:
        fp.write("birthday=%s," % str(people_obj.birth_date)[:-9])
    if people_obj.death_date:
        fp.write("deathday=%s" % str(people_obj.death_date)[:-9])
    fp.write(")\n")
    couple_obj.append(people_obj)

    # 为后代写txt
    kids_list = set()
    try:
        for i in People.objects.get(name=people_obj.mate).kids:
            kids_list.add(i)
    except:
        pass
    try:
        for i in people_obj.kids:
            kids_list.add(i)
    except:
        pass
    kids_list = list(kids_list)
    if kids_list:
        for i in range(len(kids_list)):
            kid = kids_list[i]
            date = ""
            try:
                kid_obj = People.objects.get(name=kid)
                date = str(kid_obj.birth_date)
            except:
                pass
            kids_list[i] = (kid, date)
    kids_list.sort(key= lambda x:x[1])
    if kids_list:
        for kid_tuple in kids_list:
            kid = kid_tuple[0]
            if not kid:
                continue
            try:
                People.objects.get(name=kid)
                fp.write("\t" + kid + "(id=%d," % People.objects.get(name=kid).id)
                if People.objects.get(name=kid).sex == "female":
                    fp.write("F,")
                if People.objects.get(name=kid).sex == "male":
                    fp.write("M,")
                if People.objects.get(name=kid).birth_date:
                    fp.write("birthday=%s," % str(People.objects.get(name=kid).birth_date)[:-9])
                if People.objects.get(name=kid).death_date:
                    fp.write("deathday=%s" % str(People.objects.get(name=kid).death_date)[:-9])
                fp.write(")\n")
            except:
                fp.write("\t" + kid +"(id=%s)" % "".join(lazy_pinyin(kid)).replace(' ', '').replace(".", '') + "\n")
    fp.write("\n")
    fp.close()
    return couple_obj
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
家谱软件是应海峡姓氏研究之约开发的族管理软件。 该软件是最新开发和上市的族管理软件,经过专业人士的指导和专业团队的开发,是目前国内性价比很高的专业族管理软件。 该软件具有管理大族的能力,扩充能力在千万、亿级别,适合修全国统。可以修全国各个姓氏的族,对管理姓氏无限制。在同一软件上可以管理本族和其他族的资料,例可以管理外亲等族的同步资料。 经过近三年的专业团队开发已经具有完善的族管理功能,可以帮助专业的修人士完成建、调、出等复杂的工作。该软件已经经过专业人士的测试,指导,能够完成大部分人的修需求。 该软件采用最先进的开发技术,采用国外最强大的单机版数据库技术,具有无限扩充能力和功能完善能力,该数据库支持的是上亿级别的数据处理。其技术为微软的开发平台支持,是其他族软件所无法比拟的平台和技术支撑。采用微软的visual studio开发技术,该技术是微软的最先进开发技术,从微软创办至今一直完善的一个技术支撑。 单机版本,数据库为单一文件,不是其他族软件的一堆数据文件。该数据库文件可以自己备份管理,可以拷贝到其他机器硬盘等来备份。 该程序可以脱网来使用,不需要上网,完全属于自己的软件,放心,安全,不受任何网络和第三方的控制。 该软件是目前市场上唯一直接对树操作的族管理软件,可以在树世系图上任意增加族人,选择某一族人后可以增加兄弟或子女。增加族人人数无限制。使用鼠标右键操作,符合大的操作习惯。可以任意修改族人在树上的位置,方便在填错父亲的情况下将其修改回原来在树上的位置。在树上的位置可以整个分支改挂到另外一个分支,调整族人分支很方便。适合先建族人后修改父亲等需求。 该软件一改以往修软件的特点,增加了输出word文档的功能,方便在打印前对出资料的二次修改,具有很强的可修改性。另外,可以将族人图形资料输出到word文档,和族人资料一起一并打印。可以打印输出族人的图形等可见信息。 可以在数据库内保存族和族人的语音、视频、扫描资料等重要资料,由于保存在数据库内,所以具有保密性。家谱资料在数据库内可以删除、查看,方便整个族资料的管理和完备性。 可以将世系树的结构输出到文本文档,对于族的分支结构在树上得到很好的诠释。由于文本文档不具有格式性, 所以可以输出无限大的世系图,可以将整个族的世系图完整输出 。 具有输出部分世系图分支的功能,可以分段输出世系图,便于将世系图分段保存和管理。配合整个世系图和分段世系图,对于某一分支可以了解自分支的世系全貌。 可以管理多个配偶信息,配偶信息的输出是附加在族人后面,对于庭的完整性得到很好的体现。配偶信息资料全,可以了解配偶在自的排行、兄弟姐妹等情况。 自定义家谱输出格式,方便输出古代的五代格式。五代输出按大排行方式输出,符合大的排行习惯。 族人输出按照大排行的顺序输出,例子老大的儿子输出要排在老二儿子的前边, 老大的孙子输出也要排在老二孙子的前边,以此类推。 可以管理族的历史事件,历史人物,可以任意增加历史事件和历史人物,来阐述族曾经出得辉煌。增加的人物和事件无限制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值